Load the lead's scrollback 50 turns at a time

This commit is contained in:
Ian Gulliver
2026-10-16 18:11:00 -07:00
parent 5dddc15ab9
commit 859aa2dd8b
20 changed files with 625 additions and 238 deletions
+26
View File
@@ -57,6 +57,23 @@ paths:
type: array
items: { $ref: "#/components/schemas/AgentSession" }
/api/me/lead/events:
get:
operationId: listLeadEvents
description: >
The calling user's lead events, oldest first, a page of whole turns: those after the turns+1'th turn.ended
before the before seq (or before the latest event when before is absent). Page back by passing the first
event's seq as before.
parameters:
- { name: before, in: query, required: false, schema: { type: integer, format: int64 } }
- { name: turns, in: query, required: false, schema: { type: integer, minimum: 1, maximum: 500 } }
responses:
"200":
description: Up to turns turns (default 50), plus any turn still running when before is absent.
content:
application/json:
schema: { $ref: "#/components/schemas/EventPage" }
/api/me/lead/messages:
post:
operationId: messageLead
@@ -1509,6 +1526,15 @@ components:
payload: { type: object }
created_at: { type: string, format: date-time }
EventPage:
type: object
required: [events, more]
properties:
events:
type: array
items: { $ref: "#/components/schemas/Event" }
more: { type: boolean, description: "Whether there are earlier events than this page." }
Focus:
type: object
required: [tabs, tab]
+3 -2
View File
@@ -196,6 +196,7 @@ Each user has one long-running lead agent, shown in the left pane. It is not tie
- **Sessions.** A lead session is an `agent_sessions` row with `owner` set to the user's login, `role` `lead` and no `task_id`. Its events carry `owner` and no project or task. It runs in `leads/<login>` with `leadGuidance` (passed and re-attached the same way as a task agent's guidance) and the lead tools instead of the task tools. It has no Stop-hook bounce, no snapshots, no end-of-turn commit and no turn summary; a turn that ends in error records the error on its `session.state` event, which the pane shows as a note.
- **Messages.** `POST /api/me/lead/messages` sends the caller's message to their lead, starting a lead session when there is none. While the lead is mid-turn the message is held in `lead_inputs`; the lead's PostToolUse hook (Codex: `turn/steer` after each completed tool item) hands held messages over after its next tool call, like a task's queued messages (see "Agent sessions"), and any still held become its next turn. Signals are never handed over mid-turn. The composer stays enabled. `GET /api/me/lead/sessions` lists the caller's lead sessions; interrupt and compact use the usual session endpoints.
- **Scrollback.** `GET /api/me/lead/events` returns a page of the caller's lead events in whole turns, `turns` of them (default 50): those after the `turns`+1'th `turn.ended` before `before` (or before the latest event, so the page also holds a turn still running), with `more` saying whether earlier events exist. The lead pane loads only the last page (see "Events"); when its top comes within 400px of view (an `IntersectionObserver` on an empty element kept as the transcript's first child), it fetches the page before its first event, draws it with fresh render state into a fragment (`prependEvents` in `web/src/task.ts`), inserts it at the top and keeps the scroll position. A page starts just after a `turn.ended`, so rendering it from scratch matches how it drew as part of the whole history. Live events still append without limit until the page is reloaded.
- **Signals.** Every published event goes through `Manager.Observe`. These become signals in `lead_inputs` for every user who has a lead session: `inbox.created` (a question with its choices and recommendation, an error or rate limit), `inbox.updated` when a human answered a question or error (a question's signal also carries its text and the agent's recommendation, so the lead sees where the human departed from it), `task.created` (naming the TODO item it was started from, or a subtask's parent, if any), `task.merge_started` ("<task> is merging, started by <login>."), `task.merged`, `task.discarded`, `turn.ended` with `summary: true` ("<task> finished a turn:" with its `result` cut to 1500 characters), `task.workflow_stage` ("<task> started stage "<stage>" of workflow "<workflow>".") and `task.workflow_finished` ("<task> finished workflow "<workflow>":" with its output cut to 1500 characters), `review.started` ("<task> started a review of snapshot <n>."), `review.finished` ("<task> review of snapshot <n>:" with the review's summary, `change` and findings, or why it failed or was skipped; `reviewLine` in `internal/agents/review.go`), `user.message` on a task from a human (see "Pane lock"), and `task.state` ("<task> state: <from> → <to>.", or "<task> state: <to>." for a new task; see "Task state in the store"). The store hands `Observe` all of one transaction's events together, and they become one signal, its `task.state` lines first, so a turn's summary always arrives with the state its turn end left the task in. The human switching the right pane is not a signal. A signal caused by the lead itself (actor `<login> (lead)`) is skipped, as is the update from an answer it gave. A task's `task.created` event carries the actor that created it, separately from its `created_by` login: tasks the lead creates with `create_task` or `start_todo` keep the human's login as `created_by` but have actor `<login> (lead)`. Whenever the lead is idle, byre sends the oldest held message from the human as its own turn, or else every held signal as one `byre` message headed "Signals since your last turn (n)", followed by "Tasks now:" with the `list_tasks` text and a line saying what the lead's right pane shows (task or project, and selected tab) and whether the human is working in it (`leadStateText` in `internal/agents/lead.go`). If collecting that fails, for example because a project's repository is gone, the batch is still sent, ending instead with "The task list and right pane state are unavailable:" and the error, which is also logged as `lead state`. Its guidance tells it not to respond to the human's own actions, and to make its final message exactly `NO_REPLY` when a batch needs nothing from it. A transcript (`quietReply` in `web/src/task.ts`) never draws an `agent.message` whose last line is `NO_REPLY` (the model sometimes writes a sentence before it), and hides the live partial message while its text is still a prefix of it; the partial message starts hidden and is first shown by its 100ms render, so an empty one never takes up space. A lead turn that byre starts without a handoff (a signal batch or a handoff continuation) is held (`ctx.held`): its `byre` message is never drawn, and the turn isn't opened, so the scanner stays off, and its partial message stays hidden, until the lead draws something else, such as a tool call, thinking or a real message. When such a turn ends in `NO_REPLY`, everything it drew is removed with it (tool calls, thinking), so the transcript keeps nothing of it; a turn that ends in a real message keeps all of it.
- **Tools.** `list_tasks` (each project's active tasks with their state, each subtask noting its parent, then a line listing the titles of its `TODO.md` items on the default branch that no active task was started from), `read_transcript` (a task's events in brief, the last `limit` or those after `after_seq`; closed tasks too), `search_closed_tasks` (see "Closing a task"), `read_inbox`, `answer_question` (an `option`, `other` or `dismiss` response recorded as the user, through the same checks as `POST /api/inbox/responses`), `send_message` (starts the task's agent if it has none; refused while it is mid-turn), `create_task` and `start_todo` (each with an optional `workflow` to freeze and run), `interrupt`, `compact_task`, `merge_task` (optional `target`: `main`, or `parent` for a subtask, see "Subtasks"), `discard_task`, `show`, `compact_self`, the note tools (see "Notes") and the workflow tools (see "Workflows"). The lead also has Claude Code's own tools and reads worktrees by path. Messages it sends task agents have actor `<login> (lead)`, so they don't make the user a participant; tasks, merges, discards and answers are done as the user. Its guidance says to answer a question only when the answer is clear or the human gave it, and to merge or discard only when the human asks.
- **Focus.** `users.focus` holds what the user's right pane shows: `project_id`, `task_id`, `tabs` (the tabs put first, in order) and `tab` (the selected one). With a `task_id`, `project_id` is always that task's project, filled in by the server; with only a `project_id`, the pane shows that project with no task, which only the Project tab has anything for. `GET /api/me/focus` reads it; `PUT /api/me/focus` and the lead's `show` set it, refusing an unknown tab, a task that isn't active or an unknown project, and record a `focus.updated` event owned by the user. The page applies the focus on load and on every `focus.updated` it didn't cause itself; the human's own changes (⌘K, the right pane's task picker, clicking a tab, a task link, a notification) are sent with `PUT /api/me/focus`. The URL follows the focus: `#/task/<id>`, `#/project/<id>`, or `#/` for neither.
@@ -325,6 +326,6 @@ A workflow is a named graph of stages, each a stage name, the stages it comes af
`GET /api/events` is server-sent events: `id` is the event's `seq`, `data` the Event JSON. Reconnecting with `Last-Event-ID` (EventSource does this itself) or `?after=` replays everything after that seq before going live. `?task_id=` limits the stream, replay and live, to one task's events (404 for an unknown task); `?lead=true` to the events of the caller's lead agent sessions. An event with an `owner` (a lead agent's events, a user's `focus.updated`, `lead.note_saved` and `lead.note_deleted`) goes only to that user's streams; the others never see it. Unstored events (`seq` 0, sent with `Store.Broadcast`: `agent.delta` and `admin.health`) go out live only, without an `id` line, so they don't move `Last-Event-ID`. A client that falls further behind than its subscriber buffer (`subscribe` in `internal/server/hub.go`) is disconnected and catches up by reconnecting. Every 15s the server sends an `event: ping` so clients can tell a live stream from a dead one. Once the replay is written, before anything live, the server sends an `event: ready`.
The web UI keeps two streams. The global one (`subscribe` in `main`, `web/src/app.ts`), opened after the `last_event_seq` of `GET /api/admin/status`, carries every event the user may see from the page's load onward; `handleEvent` (`web/src/state.ts`) drives the task, session, inbox and lead session lists, the right pane's focus and the Status tab from it, and hands each event with a `task_id` to that task's feed. The lead's (`startLeadStream`), opened with `?lead=true` and `after=0`, replays the caller's lead sessions' whole history and then stays live. There is no per-task stream: selecting a task opens nothing. Only the global stream drives the connection dot.
The web UI keeps two streams. The global one (`subscribe` in `main`, `web/src/app.ts`), opened after the `last_event_seq` of `GET /api/admin/status`, carries every event the user may see from the page's load onward; `handleEvent` (`web/src/state.ts`) drives the task, session, inbox and lead session lists, the right pane's focus and the Status tab from it, and hands each event with a `task_id` to that task's feed. The lead's (`startLeadStream`) first fetches the last 50 turns from `GET /api/me/lead/events`, retrying every 2s until it succeeds, then opens with `?lead=true` and `after` the last seq of that page, replays what arrived since and stays live. There is no per-task stream: selecting a task opens nothing. Only the global stream drives the connection dot.
The page keeps every active task's events in memory, one `Feed` per task (`taskFeeds` in `web/src/state.ts`), so switching tasks makes no request. Each `loadTasks` creates a feed for any active task without one, which pages through `GET /api/tasks/{taskId}/events` (1000 at a time) for its history; selecting a task that has no feed (a closed one) does the same. Live events reach every feed through the global stream; a feed holds back what arrives while its history loads and drops the duplicates by `seq`. A task feed keeps unstored events (`agent.delta`) only while its history has loaded and its task is selected, and passes them straight to the transcript's streaming view without storing them; a stored event is drawn only from the feed, so a reconnect after a byre restart fills in what was missed through the global stream's replay. Feeds of closed tasks are dropped once they aren't selected. A failed history load is logged to the console and leaves the feed waiting for its history; the next `loadTasks` or selection of that task loads it again into the same feed. A feed takes its history, then the live events held back during the load, skipping each event whose `seq` is at or below the last one it kept, so an event in both is kept once. The lead's feed replays its own `?lead=true` stream and holds back what arrives until that stream's first `ready`. Either way the whole history reaches the transcript at once, so it draws in one pass, already scrolled to the bottom; until then the transcript shows "loading…". The web UI (`subscribe` in `web/src/api.ts`) opens no stream until the window's `load` event has fired, since WebKit counts an EventSource opened earlier as part of the page load and shows its loading indicator in place of the favicon for as long as the stream stays open. It tracks the last seq it saw and opens a new stream from it when nothing (event or ping) has arrived for 40s, 2s after the browser gives up on one (as it does when `tailscale serve` answers 502 while byre restarts), when the browser comes back online, and on becoming visible if the stream is stale. While disconnected the left pane's tab strip shows a red dot and "reconnecting…"; partial output (`agent.delta`) sent while disconnected is lost, and the finished `agent.message` replaces it. A feed collects live events and flushes them on a zero-delay timer, so events arriving together are drawn in one batch.
The page keeps every active task's events in memory, one `Feed` per task (`taskFeeds` in `web/src/state.ts`), so switching tasks makes no request. Each `loadTasks` creates a feed for any active task without one, which pages through `GET /api/tasks/{taskId}/events` (1000 at a time) for its history; selecting a task that has no feed (a closed one) does the same. Live events reach every feed through the global stream; a feed holds back what arrives while its history loads and drops the duplicates by `seq`. A task feed keeps unstored events (`agent.delta`) only while its history has loaded and its task is selected, and passes them straight to the transcript's streaming view without storing them; a stored event is drawn only from the feed, so a reconnect after a byre restart fills in what was missed through the global stream's replay. Feeds of closed tasks are dropped once they aren't selected. A failed history load is logged to the console and leaves the feed waiting for its history; the next `loadTasks` or selection of that task loads it again into the same feed. A feed takes its history, then the live events held back during the load, skipping each event whose `seq` is at or below the last one it kept, so an event in both is kept once. The lead's feed holds back what its `?lead=true` stream sends until that stream's first `ready`, then takes the fetched page and those events together. Either way the history reaches the transcript at once, so it draws in one pass, already scrolled to the bottom; until then the transcript shows "loading…". The web UI (`subscribe` in `web/src/api.ts`) opens no stream until the window's `load` event has fired, since WebKit counts an EventSource opened earlier as part of the page load and shows its loading indicator in place of the favicon for as long as the stream stays open. It tracks the last seq it saw and opens a new stream from it when nothing (event or ping) has arrived for 40s, 2s after the browser gives up on one (as it does when `tailscale serve` answers 502 while byre restarts), when the browser comes back online, and on becoming visible if the stream is stale. While disconnected the left pane's tab strip shows a red dot and "reconnecting…"; partial output (`agent.delta`) sent while disconnected is lost, and the finished `agent.message` replaces it. A feed collects live events and flushes them on a zero-delay timer, so events arriving together are drawn in one batch.
+277 -209
View File
@@ -673,6 +673,14 @@ type Event struct {
TaskId *string `json:"task_id,omitempty"`
}
// EventPage defines model for EventPage.
type EventPage struct {
Events []Event `json:"events"`
// More Whether there are earlier events than this page.
More bool `json:"more"`
}
// FileChange defines model for FileChange.
type FileChange struct {
Additions int `json:"additions"`
@@ -1294,6 +1302,12 @@ type ListInboxParams struct {
State *InboxState `form:"state,omitempty" json:"state,omitempty"`
}
// ListLeadEventsParams defines parameters for ListLeadEvents.
type ListLeadEventsParams struct {
Before *int64 `form:"before,omitempty" json:"before,omitempty"`
Turns *int `form:"turns,omitempty" json:"turns,omitempty"`
}
// GetTaskChangesParams defines parameters for GetTaskChanges.
type GetTaskChangesParams struct {
// Since A commit to compare with instead of the merge-base, such as a user.message event's head. Not allowed with snapshot.
@@ -1433,6 +1447,9 @@ type ServerInterface interface {
// (PUT /api/me/focus)
SetFocus(w http.ResponseWriter, r *http.Request)
// (GET /api/me/lead/events)
ListLeadEvents(w http.ResponseWriter, r *http.Request, params ListLeadEventsParams)
// (POST /api/me/lead/messages)
MessageLead(w http.ResponseWriter, r *http.Request)
@@ -1867,6 +1884,52 @@ func (siw *ServerInterfaceWrapper) SetFocus(w http.ResponseWriter, r *http.Reque
handler.ServeHTTP(w, r)
}
// ListLeadEvents operation middleware
func (siw *ServerInterfaceWrapper) ListLeadEvents(w http.ResponseWriter, r *http.Request) {
var err error
_ = err
// Parameter object where we will unmarshal all parameters from the context
var params ListLeadEventsParams
// ------------- Optional query parameter "before" -------------
err = runtime.BindQueryParameterWithOptions("form", true, false, "before", r.URL.Query(), &params.Before, runtime.BindQueryParameterOptions{Type: "integer", Format: "int64"})
if err != nil {
var requiredError *runtime.RequiredParameterError
if errors.As(err, &requiredError) {
siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "before"})
} else {
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "before", Err: err})
}
return
}
// ------------- Optional query parameter "turns" -------------
err = runtime.BindQueryParameterWithOptions("form", true, false, "turns", r.URL.Query(), &params.Turns, runtime.BindQueryParameterOptions{Type: "integer", Format: ""})
if err != nil {
var requiredError *runtime.RequiredParameterError
if errors.As(err, &requiredError) {
siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "turns"})
} else {
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "turns", Err: err})
}
return
}
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.ListLeadEvents(w, r, params)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// MessageLead operation middleware
func (siw *ServerInterfaceWrapper) MessageLead(w http.ResponseWriter, r *http.Request) {
@@ -3274,6 +3337,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me", wrapper.UpdateMe)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/notifications", wrapper.UpdateNotificationPrefs)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/lead/sessions", wrapper.ListLeadSessions)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/lead/events", wrapper.ListLeadEvents)
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/me/lead/messages", wrapper.MessageLead)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/lead/notes", wrapper.ListLeadNotes)
m.HandleFunc(http.MethodDelete+" "+options.BaseURL+"/api/me/lead/notes/{noteId}", wrapper.DeleteLeadNote)
@@ -3335,215 +3399,219 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
// const string: with thousands of chunks the chained `+` fold is several
// times slower for the Go compiler than parsing a slice literal.
var swaggerSpec = []string{
"7H1/cxu3kuBXQfGuStbuiHLey97dSnV/KLaTeC9OfJZyqb33UlpwBiTxNARoACOKcfm7X6G7gcEMMeRI",
"thRnc/8k8nAGaDQajf7dHyalXq21EsrZydmHiRF2rZUV8I9Xxmjj/yi1ckI5/ydfr2tZcie1Ov2H1co/",
"s+VSrLj/678aMZ+cTf7LaTvqKf5qT3G0jx8/FpNK2NLItR9kcobTTCf+B3rXD3WxEMq9NboUFv69Nnot",
"jJMI2lLw2i39X92xrGvKG7YSXFnGmWuMYtIy0ygl1YJxVTGlmXVG8BUTt0I5tuSWcWPkrajYXBsGI1zz",
"uRPm2opSq8qeM3EnnahoXLcUbI2A+cEXWonppJgI1awmZ3+b6JtJgXD4h/Dh5Ndi4rZrAQAaqRaTYnJ3",
"4j84ueVG8ZVf1N8m38OifvLf4Z+XNAr+6xWN9bGY1Ny6a4D/mrtdLPyyFKoDpn+fbYx2gnFWSyWY00w6",
"S6iYFJO5Nis/1KTiTpw4uRKTYqIN2/ihpGPWceNENZ2cqaau+8v5WEzWsvKA0HOpnFgI43+wwlqp1XXn",
"9/Y767gTQFmWacVg2y/xkzhZd3VXS8Fo0CO/Av99BiDH7c3QpLdCVUjavZ8+FhMj3jfSiMrvSAJ7O2D8",
"HBcdltDflSIQabv5evYPUToPQLrMXeoujeBOVLS32a3ZWVL4ZrbNrniuzU0csUct0i0Z/T43enUGpINr",
"PLJMVkzP4RFQkVv6k+T/6T9hN0KsbcE4Wwlr+QKeshc1byrBXuhKFOEYwnNdibtpDvpk+l0AL2CqI8vm",
"0lgiRWaEbVbCn0ZpCdhAFQUrG8e4Y3HRBWtUJQzjTIkNvX3dbu454zPrmYEHUruliEPZLLQDVLXSlah3",
"wX8n3jfCAv/wL5wzsVq7bcJMIqorMedN7absIszPtKq3zG6kK5fCsplwGwEH0jK9Ue2XMHIeWL1RwuxC",
"5U9RrRdSsc1SW8FqwSvGPVkiSqUNgHqkcOap3x+3cDQzMxldi92JjLiVYiPMGeMM/26HYc+sEOwdPD0+",
"35nFslpa50G7hZ0WVjDduJTbytW6FiuhnPDn0S9iUsQpd/luynDiEFXtTxRdEpMCrsRajOfb6WG+9KO/",
"xhF3nr+LU+z89KKds8u7endlsiOwY/v2o+Vy3UG+1XWtN5b50wYECNQDf4V92XDLiKOwjXTLM7ZYu5N/",
"IjrzF6pn1nCeC3+Rmi2dGxzK/9bygHS/SngMSK7EXXaDdo7n7gqSwRNiklXBSk/Mis22bLY14pxpA+sC",
"SJlbGo8z/x6SdqOc7Cw8MhngWcBpLJw2/DSD5t6N0bsp8PAlNwbyiNwi23sEzlGHoxfplZC9T5zj5XJF",
"QlrvNuGEtgzLEqq69vJA/uqWK77InGh4fMYq7jj7+d0PWdq7kapKD9lcwpoqOZ9PwsD+n3xhQP7wKPMo",
"UHxtl9rfno2ps+SxEmbFc0RBg52xN/gGs7oxpciCt+Y56dEDecaMWOsTI2ru5K1g/s0poyVzhX8x/yaT",
"KGQRz9poc+OM8DfezOq6ccITXxwHBK482/Qy8+5i5vMztpAOeCaQt+FqkV9NwNoeGcu4Pfu8T1Ty23BQ",
"ToLdztHlN7WeZSgS9YlrfP3DsDDzGQWg7PJyh7cD3P1O4UtpXilntrsrlgulYZL+Pvsdxh9RFhD++zM2",
"XUiHj0Fp8b8gfxV3Ze1ZnydAy1bclUsvnsOFUEkjSqfNlvGZvhVdeptpXQuuPJj+4srTifxNDJ0K0Bbk",
"b0D1s60TIGzEnZHK/bev28lS8X+7qqW6SeZLIAkE0FdfuGuRwaQt2ByuLNDhGI3InH/NOr614SSGIzhF",
"ZJ31X24Fifiqx9xaS+UsU3qzFEYUhExxK0tRMKvLGwEIXst15yqrpGfoxNhgwpHyQiCTq+1avIRB0iff",
"4oDpo59w8D65wj7SjC2iaRuLSHMDlPqDtM6DuEOrHuuBbJ1Y2UOKfaT6uKETbgzfDvNZL3tGYi06PLKz",
"N0Zrl0qh/kf/LMsEnWlU6U/ngflA41/5k0UL9bSh2EYYwYzw976ocuemh3xYWRGRlc6fQ3i0pXRxTVrT",
"YdYUXsyOfZu997lfbvacg5h/fUAxfwgPDrf+WCXk0p8sFWRZAOvIomHGAuPjrLHCKzhzXTZ22qz99BVq",
"qKi9uKXYspmotddK9ZT95BUmOO3+Sya9eAg6jVgNSALbWvMU6haxa6P9n4PXqnjfQc0wExy+YHcMDu8n",
"hMaCNrAF8eD941nHi6UXEzLUUFXSY93mRYCZVNxs81y6ErXY86muq+v8OTfCsyjaLv8Km4m5P3lwkOHH",
"vdJZVndrbCpX8qoSFcrVci7hTwBXoB4I89MU1yWgJqPVDZxtmq1IcJciIyJtcCtas2l3L1pk7/IplCyt",
"OnLs56tvT/4Hc+LOncN/PTEDM8xf68NYo2t9DKGKO5cdYw93BdCWuq4sWitAnQIbDdmN/JLGc1S6vQhH",
"6cwE3hC6X3rdYgfXay8iZZc0gK88VDhMfmpVSbW4DGaFvvB05w8A8iCd6gtka9FsLu+mJANWZ6ySdiWt",
"FVUBDLAWc8d0E3H5RpiFYBtulFSLVBqBibK3/j5RhID/lj6mf74OY/SW9zOw310cR5PKPjGhg6cdvgdP",
"s+j1jD+zrR3WnJjvFJjjBw3H9B1yILlYOrbmSjC71Bt7nm5PeBEM4MS/8TZxeWvz7BACrri9ueIzetvm",
"j7//BW83fyOuG4f2iMKLuNpUwkzZVVQGPFj4BcrH8ZYbJbel8PTEtkH70xUhaAB/O/twiNMCIhB7uc1/",
"rWb67rUTq8x99lgyzMBQfYMG2HQ9UloPhwAZr5gY7sR1LVd5109X5uiiNwx6xkALvXPs2Rtubiq9UWCG",
"A7SXSy1L4ZUe8ORwy/7jhD1jx3BH/Ac7hX/+U/jnWpY3J1qBovMfJ+xv7Nf0vb/d/Zq+x9UWTa5AQMcF",
"CwAVbLPcXs9qrwxVBdMAsGXPBC+XBNCRhZFaSj0u2KqpnVzXgj1zpkHfQJjn2Mv9pV6thKpExZ6BHZKb",
"G1GFBR7BeMcF49G4Zaesg+wzBv+bshTlZ2QWL5KnOH40l/sfTvAHvOSPCxrp72pS3FsQNMLq+vaedBY/",
"GjBYdFyxow40nJZ39FnuWI/i0zAKcen9piG0iV6PtqwkWExtpDsnOR05CsOtEBzMpMnhTrE1yEcQM9Uu",
"K/lETL8AOMA0KdVr/PyrPvJ7+BgNrs2J8u15GA1xYiDOEMZD+GQlankrzOfhrn4NQ2RGzKaz1F1a7N9g",
"g4IsmZxGb++V/+BjMfHq5EhCD6uhj6KJJt24g8pcjsaegBRG7kT33sIfzsL1ZIUKV8Q5E3e8dPWW+TsI",
"HVfxSmpvF7qgwCyTvOTvo/BSR64Zvf89ONEsCGKev11TsRzY0JTgRqvEDgxn9DOvk+852B6S61n4SzIg",
"IG+rehgR9mktkhmMd5CIrmjWGKmyJgEGjZfFhHSPrMySXAudIYQfgCu78aygHQP+ts1aGCuqATHoB8Gr",
"H3WOrD+j3DbICMiWdI85sq4+P3zvLkpGzu3JGzTlDR1pcDBlztg3tZ4xWZHbtln729A/K3ap2MuEOAwS",
"p5WViER/v3M0ENPwBty8aJOVFr2loK5yJ4w/zXbKfqHQoUrO58Ik7ubW1wqjFx3PcxLrEE1FIbaEjHn5",
"I5U98G/4ls0EmZAxlonwYgRbyFuhRugoQ0aHH7WTcwqGe2vE3OZcGbJcQtCJ9OcH5WrA1FwqaZeiAtxZ",
"pvxYW8QmGi7BtCkN80cMnNls7SGfoiCV2uu94JqZuyMp48x5w1Hgbpkx/nf4acp+Aq96CFHJDtTK25mh",
"UhF9HzRGrLXJDXAFeidgC+y7QmFsAuPMNqsVN1v2zP88RY0CfqIfzpjXP45HGKFaXLSQFAHF3RXmSOIt",
"V2LAYlEZPg8Ol97CvG60WcpaMI73CpCK6ivZaGNrFJ53Osq7CARTedYN0s7jR54ZvfGUtpGq0htwi8Cn",
"0eNIAzGBETb++EkVeUkL2Ai0BpiKFg159BknS7nmOatpJe265tvrQfflQrprseKyHvw1fJoLqtmEoEk4",
"fksOJlgrHOPghpeVUE667Tl6HJx/eyEcU5o5w2UtTJYtgZPisNiIrxXdNSYwp4vLIg5Dnj7PTTqIYH8c",
"cuF5/nkvGgJMBf5qMreiYtyxpXNre3Z6+vfm+fO/lp6fHVm21NbBA3GGz/1I+OB0+mmelFa/JAQC8Ael",
"7reooz5JTCbx0uuZ4WrARj0g1AzukBErEqgyP6319Tibd4q29rMdgON094uSIBQPyT971jZ6ATuwD8Dh",
"dKnrAdfsvWw5HI5e2bgQWI1RZF+z/yW/2WONDgY8I0ohb6Onqrdsbgeitw6FVmfDOcK8R5bNm7pmtVAL",
"t+yEdeweu7EB0zHOrWPOIQsNrAORRcAd3BabCx5qSNsNbjlev+28sQt+z+uM8f8eCWhr5xVfe7m11E1d",
"eba/5saCQlpJ+LcRpV4oaQWzUpUCxTEKiS/YjdiKygtHuPppzoLod1i5/G6stIUp/B3rRefCC4zCkul/",
"tD2/S88fD1igCI0RstxWvIuXSo/jK9Iz2ijlJJB518lVaQG3aWNFQTHAUi1A6Q+ha0eWoXt2V77NWfp3",
"kRhiftt40PNU7Lc3cr0W1XQfm77PiReBafTcfV4SqM7YZrnNx7mjE+wabJh7iXi8K61P3t+jI4gtmxVX",
"jFeVERbEOF4uGQGQEK3nVlJVAiROI6yX7ukte854+MJrKOAbEOAG81pJltCDXvM5dPcB5fPVuG3FtWRC",
"K7USZxSC0NJMM1tJd41PGDeLBoxqZ61qERwxx0F9AwwViFZUNsStMNJtC1ZyJxYQ2+Skq0XBKuG4rAsM",
"ggBFWXbk5hZ/MZZTDaTT5P3MhAhcV3KorBNrOlmQ+wRxgwWz2v8jrJ8ZrlJfchsK73E1KYisPdfGaUZ6",
"l5F/9ELfk4cvcfTkybdhouTZZZhzdFLPA8hlfxh3silZf8VOeHfwUQSXYMc+1GMCOc5LqQG/h41obfRq",
"/VAr0a7KAKONCLPA97K4CDHhD/QB65U/2XbJc6oL/grRFUYI/xblw6y5IZ03DUk8suz7VxcvY1rchlvm",
"+I1Qn+9iucU1vR92vh9ZsLNZRymMUXMNZApgkXhC1m4jgv0A42Cti+tizbqNTh8RIzSkk+wC/FZbuNt6",
"umEA0xZIp1+dEzeeQ7KmmNtTL2Cd+tft6Qc6hh9P/XenH9TH+2qGxSTs7cCPcrHIRSe6xqhroaozBumg",
"jJRWoaoTPT8BmyeSzzRaKJ+15wvTLcGcg5+nPg6ndc1KXtf2uIB/XPt/FMyvge5tVTFPI4bx9Vpwg7FV",
"WrXYQ8LzV7jgppYewEbWlU05eQgbLeJi/J9hPs/MwoT+uZ9uJGsPh/IKkfcmztP74aox6hXM2v9B6/oF",
"wtD75cLefE8Q9b9BAA8w6wx/VpMOH0gIIj1wLSkc1GEvHc8GWfqNzh9c64DXInPRjVs3jpIX4Re2EP48",
"zLbMo/g8ptxapr3CweuaIsBWbMlvRTQfT9kFxtykucniVuomDExnD8I6noUo6phJuRAFhOocd9MS2+mj",
"1X5mpJhjfMT4i2HGbTbAX5BlHyA4shhThGYFgXNOAweGI0WWh2NyMSLThixIr0DD8cLVRrM6dxDIj85E",
"/A2ENL/Ygm0iBJ4Dzmu9SXkmAbDiknyaFLnQpmgSrFP2TszBRgr+wmQaPOVLrvoQ+rlluWS83vCt7a6Z",
"JUsNMwDCw3FGlHg5g0s19qT6Wb/hVrwNH8cnb2CUYUE70m0MzSaHTViC1eAjxRQ5FII+OVNw0NjVhe0n",
"ReYTPWdf+fvrq+fPvaBreOmEsQVrlHzf4G4k6SF+q/Mxx1FQOYQDqawzTTnk3c2booJguE/O8bNcOCcI",
"jr7Zw9P8QPY28pPO4YinKz0S2ZVX0pbcVNFO2U9Eo1/hRiprwZWorpu113E1a3+V7hzNIoHFwKvNet+U",
"WW/IL0sB9MHZS3yrczKDbwIYBoZJl7X2J3ArBv0he3LLk4BG6WUjwD7MiJ/BuewENqZsC9kofZVPFVee",
"0efnpe/6l0JbbQEmx+CItcdIy0VAMfey25Lj4cNcxyM72loE1PbaQ5fj2wjLUIS6p6drJC58sUWKJSil",
"8zdaRBwm2koXiDEfFjGg25I6ijloKFxJy1ayAhlsymRVizPGSydvRZE4VwlEcuWgmB5PQ/sBXCO4DCNK",
"7anZC3Rm4f8fCfwsoXVyTeNQUi2mnXORnBhARXzNi3d4RiotbNDGTKOObBLQa1t5fi4Vr7un2HYvhFZT",
"pyz2sLwJJMouQkgGAuTlmgjo2OR2JNJ+/nryNGS7J4++baFIH78JEKUPXybQpc9fIKQ/rw8q/mBjuY9H",
"CD9otfSEHcUTO8ih8czkYuAXeb/F+OQfR6I6fTAAgst5l5GYr4GA8oYjFIupIkzOePAKLmc8X6FwDFCr",
"14ENL2+AhD2dtfUZpIJ01Go02+mU9ckwnmp2DW6IkfkqScmV8dlYtccUrDODhYt0/cKysjFebKq3oYpQ",
"XvskCXEA92syzV+3sRqjLfk2E1lxwPPRkkvG9YOWg50IaLRjkgUfIx9G+zYQMdcQm5G5MyBkI9RgUnpT",
"MF1XHojgPrifn8OP907MswHOVlyXtQw7m7WemvtaZDIVoXYX+b3eMIgD5HGlYCBY8S1baJBCdeNAR0iK",
"T9FFIh1DdwwDSacpb/JE9ptezWTu6F6G80gR9AEE0nhYDZUMkKbxhuQ9MoeD7oUJpdttwf0etSn/F0A7",
"6HVK8J+c9Z1znJynosvaunvcPct9Suwem10O2CI0v8m75zbLkdFB8E7wTGj5oB3PivchpSuJWQr2PC8O",
"BscDSL9LvVGjzHOZfNIc0Ffc3mTyErkV11SOYucY7IlSQAl8fykyELL9WlAsQecq3fzToQJkGWNunTPx",
"vyb/FYlUP78OJoU1r4VzXiq0Vi4UhqKAbQeieTnzAzbGUz2SGX4GllKMS/TCNwQKQhaeP+Kec/GFzh/R",
"xwgTeRxliUo6EJWFUnkbLp3nHDHdPmFmRfgXWFYqMGx0Z2Az4V82YqVv81n0D6hEpsJBieZt2dHQOmpt",
"shX30Pv8UvbpfIl0fq+CYJ7Ur4c0dzwI5LF733C7jN4IlbEEddzZDuxXG+DXVJFN48nKwoeAvG9Es++Y",
"htxSy0wsWUZhiSpJOJQqtYnFMy3VIuIMbJYYueuHDJZMKieoPLqlZUtR3+Po4xqirJ/ZTv9TpJUOQnFl",
"K34j7FlLr/hBWwMqxDRA7QFcMPl13XUy+yCGD5zQFr3p+STttFVLodpUt3BlEcpYwXO8Kvy3QTrHxcKL",
"6wb+IGONtqFYn19w/jwS5Neee16TgXHPKnBHAcsS3OZEExT3REbbLhEFLkE/0pLpIpBu2HSDH1wfTkfF",
"KxM3MNQtiyc7mEgQ1nsdbC8GX+eN2FfRjB7u8STy0T+D4GH2LA7hj2j8xz//63HB1hwCMwh15Jb1uBJm",
"Bao/t+ztT++u9qocBwtrDhTXCSy70hQPsnZezkdLDkil5FTkK+H1P2U52Dw9UGjHxbACBJ89w+J9U5iW",
"BJmSGyiwQlmi/u3js5wAgAwD76aCidoK1t56RWAvRTAzOSDAiLPWHwpybqxhh4ySO7r6vOaahh+FiGuK",
"+TCNsuHn67XAoJdQcBGLWKLzD7YICKoNs6Ax4GBDaJOhq5AiDgqaHRKjUV6d7jPjdMGID/DvwGt6hous",
"vSfMNtLa4+XCnqknPiI7T/sKgPQ2gtj7offwTQQ6PnqZQt95EUDvv9d9+KO+wHXly4KAMJdHyv0w0diL",
"MFb7aAfGxrZAAkQoVl2Pkv87Lv1dbWAnj+GcPY8BCKiyYSzRmPoeA6ayYuJ0pa8lpdofuFyvfnr503SF",
"WRxd4T41XN+L0QbfTH52vyth8uiwixPPjf5NpII9e7YQzm/NL+HdYCYv9Xp73AGME/CkoOerFrTwXZtG",
"DQiu4RWUMGRFXBKG13O4icAb1luGXXIj9iPlOhoXD06bYgoNx5ig5YHwHG5wHvCI3yM8vJvMTQbVVEaL",
"GmN//KLVM9MLNih3Sd2doajy1nK7hxX2ZZruoRxSirF8ks3rxnniDEK7wSibUq/8jKFuapSCT0AGSM48",
"yfVROuqK+wWGGkfRr/Xqt7cO1LrLh5nW98hmT8pG5TJpjcgtHGrnzbv12vwhmyPZo1ICRdWADKu4kMPw",
"98iNqAPgCEsb3L6BgLlhvjeO8XBm+a3f08h8NBxmqawTHCpkI/OhsObIU8Cy4C+qM5SCOpEIqUfP686l",
"Xgkbizv7MSiAtGJ/n2CCDPyTUmYYPoLP8dHfJ0WbiIwsm9vEy4QxHAzjpTEpOES4JaKsF6WRU6fVMIa8",
"KoDZof24wrI44VoGMRJQ7A80HbWwp8UkyL3ghZnpuyj32EmyUZH3jL7JZ1fpvFd89iJOfcVn39Ls/r0W",
"gCs+e00wXPHZuwjGFZ/90kJyxWchY+hXWvIvewkqEhBP7640nJD4XIFqC0YYLjHAsJcPJ7zSGEr77k4V",
"AHkZ3/ND/vvFmx+m98j6yoczJHNnt57weEkZKP1QhhqLH0olV54wvsqJKkZvDr61k9NQg9XXf5kFS1c6",
"U+/yTlpnh3XeXd6MTSOiGMQxFKlXmTJRYyMXHuff0JWGmkeHjOkEeBh/aMX5+kkzXW2p7cJuiSE/4BGm",
"BSiHIlW06Q0mM9FH88YA2iC9JutPEeUw0UK8JN0rSnjJyLGl4KCMYVXbAF/GHibtUNmpRPIdBhw4oAe7",
"iOKg31eInAPHCpWOCNihjNDpSB5ZIM5bBAxt2KUXorP5r2Vjcw42UacGrDb3Y8OVgzgD+jRC73X70mvG",
"5IDiii5saSGIdICOh8O8x5rlUGPQqCdMP+VyISdgxuOyNxr88buC5KD9WZEeOhDWldnUF/hDsAWnAiPY",
"NLxQezRgKs7biw6U8YwyY69ZABSahimxkEPSNgBMgUE86w5YTBqFa3NQ0Qvu2yRcALgIxEdZj4xBqJJR",
"MvHl3C0tcq38bN2q0JMRF8iKfIVBJEng69b+TAHLbjmVC3q0HPbRqebFRCWFKq7XoVLFvjtot7TFp2Ss",
"ZyEYQtpQkckHY6YH+Oic+l35KZdAEgQ6uEDqGN6LuRaJKjSj28RPTa43EPspgcwrE7wr+7deFFIQIExg",
"FUPsenHgl8F6HIpYR2kPpPi+7Jis4kNGil7oE//wxN7I9UkoenQCldI9XTvTCDS8Le6haGLU/L0KpdEM",
"+/bnXZNpoQSazlDWfbKd+8DNEECS4LhfYMyE7lWBNoSqolJ2zwr9ZH3KGfuzCWGv4/VAKziyVEZ0bGBH",
"guW4fTsmgnEBeCErv1GJyQg3qrMtnSjGcQTw4HyM7plKtOcOyuh6tQza3/XC3Q4mQYQ432H/FV2whqvE",
"cPcsquUU1KrNjUXHx3EMobpf6FQnxDwD6XBniLwziaIFzqAKimdZW+GmDNoVbKQVu/4aMnInIekACxiH",
"uq/EUOzoMYYCNYHddaJgCYxOE6fHiocFFIIH4pc4a/usdZu0z8hv0j5IomPbh9Gl0D5K/R7t025cLC39",
"ekif6liYgtmpj/7ggB1L0vlzjYettc0moCVnYN9BplCqjNi077q6v+2iHSwHDYWyZYBwQ9JHvzhwiEMj",
"NTLEnOE17P+Kv5AukS8T/MCWiqMDjqm0Ki1sFxcfQUCf68SCOpltjQBlx2Dvwsnz6fPpc6wOKRRfy8nZ",
"5K/T59O/YtH0JaDulK/lKa9WUp22vrqFACXXoxjkwtfV5GzynXCXwfzfqcv6l+fPP1srUpoh04v0Uphb",
"L1pB50bguo3lCzH1734scB2zWmP17rW2mVCJSwfNfNpWUdqwxoI7PXZJRBPsTFfbYH41fIPvn2MAyHYN",
"5oxKOFEGhxqm4W2dwBJdq8Z6BZ69/fG7gv3b21ffFey71996TvqLmL2dsguHlUX+hb2R3yDD7OL655gf",
"PkG6ENZ9Q2ahBM8A1ulCzrsIjt7GtlL/DrHhp/9Yi8VDv12rB3+6EbP1fb/tHBAScntk+NVnI0PAfIYI",
"r0IIRsU8rU394foayT83XITvNFZ++fqrv45+u0fYpx/8/15XH/cd0Ugzew/on4xwdjfRY/LI4pEl0RKd",
"0IAiOOS0uV/fY7sgLIqvhBPGSyQfJl7rD30q8Cac4B5O+tRcJGvqr+DXQAbYfifZ/hyLPAEHOr46Za+8",
"Cgv/wIaxkiKuxPtzbBNIXA46FXm99N8uf/pxiv8M6uwP3LoTeHLy+iV7Rt5C/O19I8yWxWUfQ/lOI9Y1",
"37bpWBD8EyAC1zhdhEXoS8JdCLYILYaMgMoY57g5teDV/wQ8tZ1M6E2yX5a8rqFeTNuwKC4Da1EqBr2O",
"2ELTGLrtRpRjwliMCoeY5Ld2KXgFkhVtbgdTkyJHp8Ph4TQmYLQdMspunz5UUjVlkNYGPqX2rTvftVUl",
"fz3IdJy4c0jCJ9TcunOSD55bJFH89AGHMxwi9F8OnaHXbTHYnibHvpW1332muDEU5EKlsg0QE0+S0dPG",
"vLdSbHL09YO0Ljgyc8TV24JOWMe9N/AT9j5oDeOuzrRJwQii2H9Hjy/5P+CW2yGiN9yV6E+Ber/BqCBN",
"dLvbKXsZjApOY5FfKorbp6LTzuLyQucFFOC2nRojOLfECgRQ3wpbBbPwsgHLY6cikH811DIB86PEsq9V",
"Lah7voq6fCyVC2y0UaEIeI4KqffC6+jVHxI2Hy5NdXo8jJLkfm8q8SIC1QqPe39vSe8e3Mm//a8P5WWn",
"HzyIB+TCdvmPqL0lOM7jFJyMUdpqT9zjiFqIloeJWmi1GMLmG/GYaAQH1QAGvZjj2RcILYSIJgMk+moI",
"zs9/pBN30CMc6IciJxxZQs79Tmx7qlbidB4qhQ9RAJYSf8R14gSZhcY0gZQUjuxOz68udXTHeAfVFGwS",
"kIDF/UNOSjtW22+tINeXYY7PprvScoqTz09wCTqejtYG9yCxA2D708e9Hjqk6UXxU5IE9hm7wJcV7Vqh",
"pkWPaFplqWhTJKjpay9bIw3jYb/EWvXwalL0o98WYilqzMuwobShs0yJO4qnz0gkVLHsB1Q5HoOYun0+",
"RhHVXz7b5JAcQWnhQ7TVwX+K0A0Vi9Qm4hZywT+F1wFBKU2lHbJa0RDPacmH0h0gp2ymG8eWqCJtwQ3f",
"V6XiGm+EWFssnpaIBSuxmgkTXGsL4aAQn51mVajQq+aTufEoMTJ2xhkpRQJep4MYP/3g/0eyG7bH3b1u",
"XsLzOPPOMr/e3TD8pHokwQphfqhghcsPhUoGae5qL7mFz/uUdYFR/pgP5//0eKBkQ8+EMN8VYxixTUee",
"okLdhichqi5LGEdYAQF92krjefB6aLK3AxS3yrXAif1uuujf2/YmJ3vuBio9DjPPBUT9J5RIvTB2EsXS",
"vVLdJol2QelOWmDDYPfoN6/BEIbkmy3GLyXdbPD2T279LRgWkiGlK1ppgJJ9uWIrfQuGCrC30swlt5RK",
"YeGO0LpmuI5ZjpguhWtb9zwOBbXjj6Kcr4cwH/ntA7eYsqBOjODVfj9sW9jkkxnUUMGiMSmLuz7pXoSq",
"NhhDl1bw2GEsS8hI93SDWfSh8IWeI00t5WKJNYnew3sdjpdWd8qiyzP0t23Jmsdn5iHrZAQfv6jroF1F",
"dY2E+e4iUFINIz/SKeg0f3lih2tE2oAUJTYBUZ9wxAKqTz/QXwdsZim+H+nuOLDu7po/txgXsfAwSS6H",
"ztNY5m7wMF5RtajHP4lQSmmkONW6bmAF/UYvXxL+iwGNH0+u7VS8aPNYYypojFcZKp0MIX+bNIG5CHIz",
"b/OkuWsz5mBI6XLKPAIFO/E4bCvJLX1inoXkNcywsLDLExmG8oeRsuoG9SuU+PU8TZgD6VDrmOs1TCZS",
"QWB9G7a8w0Ehre8R2SeMv8fXAE2OpXXWS8BzXlvRlnDNJA4qHRDxR+K4utIt2/3duE7ImgXPd1qGoqDa",
"vcG2DD4gsgPG0hZFqp23JXc6aY8hYRqzAWO8Xqjz3q2H1akZRx9CvRw/Uj7agxsXkzIfiVfFFMI/H6t6",
"qIuT0sxPP+AfnuJDh6fTD9Ad6+M4ug/f34vsi4Ey237+I8vWvTYqOAckHIUmVHA3PocY7oyH0sM/BqA2",
"tufXgy4d7rJNxtp+YVA1AYoEIrzQ3MJSXSXZa0bkNJvLOyibtGVyobTBt/IeoLT92SM5gpIpfh8PJPXe",
"GzhOiNInu/eDGfD0A/11QKcJVsZHxM8YBwcB+0jXbETFw67ZHE5Pyaw8jtc8EIChO/YC85BEp8R37OGI",
"V+6Sq0rP5+zZQvO6YJUoJRnIIX7LP+EVJLgVaMGNQUIFtp47pr7wyEConKHttoCXNtrU0anH4froNZvE",
"unT+5oVgeOgmQld7vNADuCQIdO/0nB6B6B8k36f1zwXoAU1Jda3pU92KWRKFDFDTrH8fIn0VciepLjv6",
"d3c28nUA8gvZyog02MdK1PIWQuV+153suPefeiMvg64PkiFQOJ5aaVs/dGxs13IGz5yCj4C0gZZV7dRN",
"7YUJYAVcypLm0HcpCX+kxiUhcCC0aANLBW/deQMkdylU1bZB+1MGFAD6lzuM6ksT9pO+ggfkmGhReizL",
"wh6NKdGWPrfkgit/mNjSRd5p2dbLy9p/XrUVELpNIKOl8Fmso1HEUnGNwr8wA09B9xH/3K9XlBA+NF1I",
"B4qCOGZQjckPVnSr792j+N5hMyV2/gaWBPXgMb4kaQyqNyo0Ugb/NnO6KaE0AySjhLp3RduQU5IilZTE",
"o4JyhxeCiURQeXCogGCAfF/5QOqL68Qa27w0WbmIjkJbNq1HYwNdVZ0Oy6C4m7ZaXndBBbON3wy/j+DE",
"Djw75BQtBa+m7EftGK9rvQloCYuJim8/qcDv2eReqvdFiiF1Ru7jQDVtTTapunvHDCfXNg8NF2+7tWey",
"AIbetnu18SJb+DN8e9ZFcna3sZhA/Bfuz1Es191tC6mVOB4E2In1p6bofKIZnogwwzbfCnMCtmKvjHxp",
"0fRPzZj1CW/ckroNDdrmwX4T248F2sU0N8poawWoDgNvazmyZxDDkdQwD58AD1ryCrKhIfoHk0RaCZg1",
"qsKAEGmgNs4xWPzB2E98Qpq+MfYilJ7aptB6rky1xqGfAwK/kC5WamOcvQhoEdWJ/9xwWecT874T7oW+",
"IBQ+iUufGydLuebKjfUntpv85UsKlZzP95Jio+RcigrKjIWCyXCa2yLscPnBdRd9jIvO1TQdurpe+tlH",
"ZcBRPbN7WW+zuXBYr/aTx6EN+eRxdF2F+st71bXHi3OXtYBtGHKOc1cu/9xMu5Jm8Ij8pASrpBGl0xC2",
"lBOmi/iGFDbUDnJL7BLuwZ2yV8rBj/5fFUjREGVXi7ljunHnLMrVFRP0LoduEFi6D61t0GbMM2KPChSE",
"asFvRbdbvp/b6GaxZNMpJqva7aqW6qagRu+NzacLxkNrDsmalzW3yxMr/EsOVYSao1NOd4tTG61dqBnq",
"Yel2dA3FW/ecwN/n1LyU5gdp3Z4qA5EooD0F7Nif/RRB3aRxZq2HzDtk0/pO3nYjY1Ac8hdZJSq//6JC",
"I4nTbGOkE61mQfGIRdsciNooUW1o2zeCQYkEKCqTGsf7fSGO4bzqWJGLW2Y1VoLut/ciS3woEkUiof+B",
"V1RZcyW4Ag/4OauMXlvG1RbzQ0AgOw9Bvn37PlvqjbjFtsA4C5YTtlmLALQ2QvU81OGlgv6R+bQfJl3k",
"2FU7GEYDFDHM039IpRyMWHGp+i79tCVO7DfDayN4taVeLnEmEmtt8P9TZyF7jm3gk45E1ASbzb2YCdwJ",
"OWraIDvD/6jyV9789JcnMz95YpR1HTsl45qq3U5z09/HdHeoVElCDyItNcKZFe/7OR1vOWoUa25j16ya",
"U0TwjJeg/MP3+XwOj8y9ZTwer+QGdLjsDLXid1Q0/fnz58X+GupPUj4BMDNGrfl57ZkjdqalTXsWDFv/",
"8vz58Zev6HidZZAkL5gRi6bmBlWbvE10yq7EnUMZiWq5Nmss/mHlbwKxc86wGBH6VelNz7hp4x5dOvPy",
"/OcVzw4IYPe6oR9VjXlBAw3wTr8jf24RDAppDR+CWBvPn4EYKryjzHjhPDYngC+w8h1+zJ3j5XKF9Z6A",
"0qX1Yr2uGwdVUIZo7ZyuASOsrm+hSxsSPhh3Q1k9qaysRLeCeK/WXgGV9gCky//zXcFKfz+CriXunFAQ",
"ezJ8fl6v0FU4pirP5z8Af9TibPZ28c93q/p3r+3mSTfUdnvQWf/rn5MzgKLw9KoZpKImTCbpVBaLFmg1",
"l2blVTT/mAwnSS4vRBxRvCKoRTTMiVTdjiFS3Upqm+I0xOpRLWTsH5uLgGLKk2FdbwtMesWWrgMKEnRb",
"9Vpl8LNhj6i289PuGrU64OYMVvGeUZ2s6HmzOTBoNLh3zFGtWRob0mKdbSgoIBceWXNu3clcmw0P9UFa",
"wMqlKG88JqGvKO1Cqi7m9MSMXojlnmOoyJ4OxqnG1/a1CJUPgtPyWa+JMjTeFe44jSmnYtXtCFL5WRZG",
"WNufNW2dfbSj3edqGeBOSsvm8i6UhIWCGW0LY1ge9FG1OzHrg12FI7RB3YXGzKGEIlRGQD9hQVGAoeuv",
"Vp5e4WjgkzYZSMZG7/3WwL33ELtnSX/gaDfxENkbbJBLMDr2jGJ45Jxqk4WSIMdFRGmADxHBkqbzWNIs",
"hTLsSpj6+1cXL0OCilBVcESENJXwmp/RIk30Gg0SXbSboo5ztoYWY71tgaobLt2OYPzoYXLKXkmiadij",
"VmjvmzQypooQQqA3A9VRzOJT86nyOb+QH9NvR3Gwt3ennbfHmX/1DHYGGzZxKnb83//CyiU3vPQXi8c7",
"3hxo50U8gfUn2yIKfs80audSoQOdmObxWQBIK7o8hhjrlDbrLFBw/CBzGs4xhb1zyNI2wR6QSeiKPbJe",
"Pe2kX9kb/Dx58pZG+pjPs3666LJ9lqjVTt90bah3/B8gvOw0iAhPL/PEgO6ONToy031k29JpEixVJBoT",
"V1sQmGpZOksBlQuhgIeoBaM//cGTtb+LLmnqBJw0zBzuopj+OWTMPnilDbKy1797BDDhPB/K/YWT8Dp0",
"4dxnaMV29zvkJu7W2gbhFP9x7V8tEpckx6vYCnNLAiiXtS050Ie5FZ2Qf/8xUw3UbtJzkJaPLFtq6wqm",
"b4Vh319dvb0s+kJRK5CkjeeHKua+bfuOPkUtB8r1GZtEjrB9+ZbQMWQT/TXwagFX+lqYJFyy3+lmd7fe",
"PeFmvbvXXv1htipTKGsXz198uaoO5xko3vXlbMWh1AjVXchQADWVPaxQycZLfbV23aSnfM4CphKkOTKf",
"P2uBRv99qieMuZkT9/kf4zoOrHEUY40v9y0K4dblsYH9vbq53whPYs6rnPbUX8I5KE8/qI/jLuJzisLZ",
"BP882ejdkruhS/oy4uEp+FGYbXTpvADdH4D5B1D9fp3Otbl5NGWlyJd6vH92+oEyEaHeAMUs5ErUgAje",
"ep+SsH88EEXs6IHl7zu8GKy3nsCkasgumQxAtem0uhXG8tB6XTpmndax/kk3OB/bB1MeSZjG78UxWv8S",
"NnWUcvVuA/8Qq21J35JuX6ALGMiq2F7Qq14BphAGZKjoHK0pjM8dJOtj55V8UZ5vtbm5bPMb/n/ZicOM",
"PSnN96jmglGlHfLl89IMgLaOnhSWQhSseD9lFxA0E2ikTvqYBJtmJKvQrFlpuGTOGcdSfAZeF9ADm/Ay",
"NZAZpTeqdVNgycsdnZ+bm6R84WNJNskMT17T8aGlHHok54RZScXrPQErv4jZpS5vhCOPOxrkUzqgMQrG",
"GbSFZnYp6hordqb+/CS0sdS19XsIN76cY2ORxJDDsH4DdONDzoYi7iZQZ5iUKLMUytVbtjb+lqjIawQG",
"7satGxKGsdVrwXhd+wcUPxPSoHHOspbI50GgVvRt71UyeFn5G7oXoHxp/HGp6wornF8RkJfyt9btJwg9",
"4k4GxyKuk9wVwHlLXQn29fPnz6NTw7/OsHtijN4U3O4NMQjzj4sy8Huyl5XsDSMbiE3zG/zwQftxDF/h",
"vdHTl6CrQEuWLcGujXa61PWfOxinUdT69mDh9b5qiV3M4UYAv9OQtbjSwqojl82++VnFxruPVxw5zDFU",
"IJl+D7fN9D/ZDocaiXsVwlhIMUqAc6N/iwHmYJYuQYD2EmFMdPWcdMp+4F4bE5Ukaz9Wkbep0ghjV1od",
"OULzgFxIvCk0/X3s/Po4z1Agj0eCiiv48rW1AOkpNYMftsiHyIP+JplGJb4dUWu1sMzps17j8x6htGoC",
"hHxgk3IQz6DDN0XYm20owMxjE2/ob5X0Vg/NoEk5wys6Sd0McoLXzQYoKG3+/4gElE4zZOJtFLavAVx/",
"geQTtn2/bfeX+NZTGFP6Hb9H2FSgkkSk4SPMYCNeFN9rvUqpzBsxcPrB/zaiDcYwc9rTBoNdta38kbVK",
"B71HKIu51Ovtg+hjqDzJw1jolvfDJ0cFPCaYn8tanMHRDxC8bJkGt+zfL978AFcIV0mULqUchjI7j9U1",
"BJu8f7o+HGxJEEi8rnmZhJ+l9YgRHV73GUYG5Gr92+VPPwZbDtQItTF+xXPor7z4+tXz5534lYSiguaM",
"lNWBpE9jGZb5tulSyxhteBShPF1FxB2esbdjVu8+v1cl948f/18AAAD//w==",
"7H1/cxs3suBXQfGuStbbEWXvZu/uSXV/OLaTeC9OfJZyqXe7KS04A5JYDQEawIhiXP7uV+huYDBDDDmS",
"LcV5uX8SeTgDNBqNRv/uD5NSr9ZaCeXs5OzDxAi71soK+McrY7Txf5RaOaGc/5Ov17UsuZNanf7LauWf",
"2XIpVtz/9V+NmE/OJv/ltB31FH+1pzjax48fi0klbGnk2g8yOcNpphP/A73rh3q+EMq9NboUFv69Nnot",
"jJMI2lLw2i39X92xrGvKa7YSXFnGmWuMYtIy0ygl1YJxVTGlmXVG8BUTN0I5tuSWcWPkjajYXBsGI1zx",
"uRPmyopSq8qeM3ErnahoXLcUbI2A+cEXWonppJgI1awmZ3+f6OtJgXD4h/Dh5Jdi4rZrAQAaqRaTYnJ7",
"4j84ueFG8ZVf1N8n38GifvTf4Z8XNAr+6xWN9bGY1Ny6K4D/irtdLPy8FKoDpn+fbYx2gnFWSyWY00w6",
"S6iYFJO5Nis/1KTiTpw4uRKTYqIN2/ihpGPWceNENZ2cqaau+8v5WEzWsvKA0HOpnFgI43+wwlqp1VXn",
"9/Y767gTQFmWacVg2y/wkzhZd3WXS8Fo0CO/Av99BiDH7fXQpDdCVUjavZ8+FhMj3jfSiMrvSAJ7O2D8",
"HBcdltDflSIQabv5evYvUToPQLrMXeoujeBOVLS32a3ZWVL4ZrbNrniuzXUcsUct0i0Z/T43enUGpINr",
"PLJMVkzP4RFQkVv6k+T/6T9h10KsbcE4Wwlr+QKeshc1byrBXuhKFOEYwnNdidtpDvpk+l0An8NUR5bN",
"pbFEiswI26yEP43SErCBKgpWNo5xx+KiC9aoShjGmRIbevuq3dxzxmfWMwMPpHZLEYeyWWgHqGqlK1Hv",
"gv9OvG+EBf7hXzhnYrV224SZRFRXYs6b2k3Z8zA/06reMruRrlwKy2bCbQQcSMv0RrVfwsh5YPVGCbML",
"lT9FtV5IxTZLbQWrBa8Y92SJKJU2AOqRwpmnfn/cwtHMzGR0LXYnMuJGio0wZ4wz/Lsdhj2xQrB38PT4",
"fGcWy2ppnQftBnZaWMF041JuK1frWqyEcsKfR7+ISRGn3OW7KcOJQ1S1P1F0SUwKuBJrMZ5vp4f5wo/+",
"Gkfcef4uTrHz04t2zi7v6t2VyY7Aju3bj5bLdQf5Rte13ljmTxsQIFAP/BX2ZcMtI47CNtItz9hi7U7+",
"jejMX6ieWcN5LvxFarZ0bnAo/1vLA9L9KuExILkSt9kN2jmeuytIBk+ISVYFKz0xKzbbstnWiHOmDawL",
"IGVuaTzO/HtI2o1ysrPwyGSAZwGnsXDa8NMMmns3Ru+mwMOX3BjII3KLbO8ROEcdjl6kV0L2PnGOl8sV",
"CWm924QT2jIsS6jqyssD+atbrvgic6Lh8RmruOPsp3ffZ2nvWqoqPWRzCWuq5Hw+CQP7f/KFAfnDo8yj",
"QPG1XWp/ezamzpLHSpgVzxEFDXbG3uAbzOrGlCIL3prnpEcP5BkzYq1PjKi5kzeC+TenjJbMFf7F/JtM",
"opBFPGujzbUzwt94M6vrxglPfHEcELjybNPLzLuLmc/P2EI64JlA3oarRX41AWt7ZCzj9uzzPlHJb8NB",
"OQl2O0eXX9d6lqFI1Ceu8PUPw8LMZxSAssvLHd4OcHc7hS+leaWc2e6uWC6Uhkn6++x3GH9EWUD478/Y",
"dCEdPgalxf+C/FXclrVnfZ4ALVtxVy69eA4XQiWNKJ02W8Zn+kZ06W2mdS248mD6iytPJ/JXMXQqQFuQ",
"vwLVz7ZOgLARd0Yq99++aidLxf/tqpbqOpkvgSQQQF994a5FBpO2YHO4skCHYzQic/416/jWhpMYjuAU",
"kXXWf7kVJOKrHnNrLZWzTOnNUhhREDLFjSxFwawurwUgeC3Xnauskp6hE2ODCUfKC4FMLrdr8RIGSZ98",
"gwOmj37EwfvkCvtIM7aIpm0sIs0NUOr30joP4g6teqwHsnViZQ8p9pHq44ZOuDF8O8xnvewZibXo8MjO",
"3hitXSqF+h/9sywTdKZRpT+dB+YDjX/lTxYt1NOGYhthBDPC3/uiyp2bHvJhZUVEVjp/DuHRltLFNWlN",
"h1lTeDE79k323ud+udlzDmL+1QHF/D48ONz6Y5WQC3+yVJBlAawji4YZC4yPs8YKr+DMddnYabP201eo",
"oaL24pZiy2ai1l4r1VP2o1eY4LT7L5n04iHoNGI1IAlsa81TqFvEro32fw5eq+J9BzXDTHD4gt0xOLyf",
"EBoL2sAWxIP3DxDCW6Kn3qG+CSa+UWcaSSpzoP3ByRqc4Hby/xGM+7PFTS39fYV7CUcM9Mo17wgxQ8eL",
"4KUJc4v1fPLF0stEGdKvKulBs3l5ZyYVN9v8lVSJWuz5VNfVVZ6pGeH5MdGmf4XNxNyzGeBa8ONeUTSr",
"qDY2FaJ5VYkKlQg5l/AngCtQ6YX5aYqrElCTUWEHGBnNViS4S5ERkTa4Fa2NuLsXLbJ3mTKK0VYdOfbT",
"5Tcn/4M5cevO4b/+5ALnz8sww1gjGWbMqRS3LjvGnqsEQFvqurJomgHdEQxSZCTzSxp/fdBVTThKZybw",
"htD90itSO7hee3kwu6QBfOWhwmHyU6tKqsVFsKH0JcVbfwCQ4epUOSLDkmZzeTslgbc6Y5W0K2mtqArg",
"9rWYO6abiMs3wiwE23CjpFqkohdMlBVx9sldBPw39DH983UYo7e8n+Cu2cVxtB/t458dPO0weXiaRa+/",
"5TLb2rmHElulAt/DoJWcvkMOJBdLx9ZcCWaXemPP0+0JL4K1ny4rvDpd3rQ+O4SAS26vL/mM3rb54+9/",
"wavcX//rxqHxpfDyvDaVMFN2GTUfDxZ+gcpAvNJHXWgpPL0rbdDYdkkIGsDfzj4c4rSACMRebvNfq5m+",
"fe3EKnOfPZTANjBU33oDBmyPlNadI0CgLSaGO3FVy1Xez9UVsLroDYOeMVC5bx178oab60pvFNgcAe3l",
"UstSeA0P3Fbcsn+esCfsGO6If7JT+Oe/hX+uZXl9ohVodf88YX9nv6Tv/f32l/Q9rrZoXwYCOi5YAKhg",
"m+X2alZ7za8qmAaALXsieLkkgI4sjNRS6nHBVk3t5LoW7IkzDTpCwjzHXskp9WolVCUq9gSMrtxciyos",
"8AjGOy4Yj5Y8O2UdZJ8x+N+UpSg/Ix9AkTzF8aNvwP9wgj/gJX9c0Ej/UJPizlKvEVbXN3eks/jRgHWm",
"43cedaDhtLyjz3LHehSfhlGIS++3g6EB+Gq0GSnBYmoQ3jnJ6chR8m8l/mATTg53iq1BPoKYqXZZySdi",
"+gXAAZqAVK/x82d95PfwMRpcmxPl2/MwGuLEGp4hjPvwyUrU8kaYz8Nd/RqGyIyYTWepu7TYv8EGBVmy",
"r43e3kv/wcdi4nXnkYQeVkMfRXtUunEHNdccjT0CKYzcie69hT+chevJChWuiHMmbnnp6i3zdxB66eKV",
"1N4udEGBDSp5yd9H4aWOXDN6/3twog0UxDx/u6ZiObChKcGNJpgdGM7oZ14n33MwtCTXs/CXZEBA3jB3",
"PyLs01okMxjvIBFd0qwxLGdNAgxaaosJ6R5ZmSW5FjpDCD8AV3bjWUE7Bvxtm7UwVlQDYtD3glc/6BxZ",
"f0a5bZARkOHsDnNk/Zp++N5dlIyc25M3aLccOtLgTcucsa9rPWOyIh91s/a3oX9W7FKxlwlxGCROKysR",
"if5u52gggOMN+LTRAC0tuoZBXeVOGH+a7ZT9THFSlZzPhUl8661jGUYvOm72JLAjmopCIA1ZLvNHKnvg",
"3/Atmwmyl2PgFuHFCLaQN0KN0FGGjA4/aCfnFPn31oi5zVkBZbmECBvpzw/K1YCpuVTSLkUFuLNM+bG2",
"iE200oIdVxrmjxh47sFOCPykZ8f0gmtm7o6kjDPnDUeBu2XG+N/hpyn7EUIIQjxOdqBW3s4MlYro+6Ax",
"Yq1NboBL0DsBW2DMFgoDMRhntlmtuNmyJ/7nKWoU8BP9cMa8/nE8wgjV4qKFpAgo7q4wRxJvuRIDFovK",
"8HnwLvUW5nWjzVLWgnG8V4BUVF/JRhtbo/C801HeRSD4BbI+n3YeP/LM6I2ntI1Uld6ADwg+je5VGogJ",
"DCfyx0+qyEtawEagNcBUtGjIo884Wco1z1lNK2nXNd9eDfpqF9JdiRWX9eCv4dNcBNEmRIjC8VtyMMFa",
"4RiHmANZCeWk256je8X5txfCMaWZM1zWwmTZEnhkDouN+FrRXWMCc7q4LOIwvuvz3KSDCPbHIReL6J/3",
"Qj/AVOCvJnMjKsYdWzq3tmenp/9onj79S+n52ZFlS20dPBBn+NyPhA9Op5/mNmr1S0IgAH9Q6n6LOuqj",
"BKASL72aGa4GbNQDQs3gDhmxIoEq89NaX42zeadoaz/bAThOd7eQEELxkPyzZ22jF7AD+wAcTpe6HvBD",
"38mWw+HolY0LUeQYMvcV+1/y6z3W6GDAM6IU8iZ6qnrL5nYgVO1QHHk2diXMe2TZvKlrVgu1cMtODMvu",
"sRsbHR6D+jrmHLLQwDoQWQTcwW2xuUiphrTd4Jbj9dvOG7vg91zsmOzgkYC2dl7xtZdbS93UlWf7a24s",
"KKSVhH8bUeqFklYwK1UpUByj+P+CXYutqLxwhKuf5iyIfoeVy+/GSluYwt+xXnQuvMAoLJn+R9vzu/T8",
"8YAFitAYIcttxbt4qfQ4viI9ow3JTqK2d51clRZwmzZWFBTwLNUClP4Qp3dkGbpnd+XbnKV/F4khwLkN",
"fj1PxX57LddrUU33sem7nHgRmEbP3eclgeqMbZbbfFA/OsGuwIa5l4jHu9L65P0dOoLYsllxxXhVGWFB",
"jOPlkhEACdF6biVVJUDiNMJ66Z7esueMhy+8hgK+AQFuMK+VZAk96DWfQ3cfUD5fjdtWXEsmjlQrcUYh",
"CC3NNLOVdFf4hHGzaMCodtaqFsERcxzUN8BQgWhFZUPcCCPdtmAld2IBgVxOuloUrBKOy7rAIAhQlGVH",
"bm7xFwNX1UDuUN7PTIjAdSWHyjqxppMFiV4QJFkwq/0/wvqZ4Sr1Jbdx/x5Xk4LI2nNtnGakdxn5Ry/O",
"P3n4EkdPnnwTJkqeXYQ5R2cw3YNc9sesJ5uS9VfsxLIHH0VwCXbsQz0mkOO8lAfxW9iI1kav1ve1Eu2q",
"DDDaiDALfC+LixAAf08fsF75k22XPKe64K8QXWGE8G9R8s+aG9J50/jLI8u+e/X8ZcwB3HDLHL8W6vNd",
"LDe4pvfDzvcjC3Y26yhfM2qugUwBLBJPyNptRLAfYNCvdXFdrFm3ofgjYoSGdJJdgN9qC3dbTzcMYNoC",
"6fTZOXHjOWSmirk99QLWqX/dnn6gY/jx1H93+kF9vKtmWEzC3g78KBeLXCima4y6Eqo6Y5D7ykhpFao6",
"0fMTsHki+UyjhfJJe74wtxTMOfh56uNwWtes5HVtjwv4x5X/R8H8GujeVhXzNGIYX68FNxhbpVWLPSQ8",
"f4WHyMJZI+vKppw8xMgWcTH+zzCfZ2ZhQv/cTzeStYdDeYnIexPn6f1w2Rj1Cmbt/6B1/QJh6P3y3F5/",
"RxD1v0EADzDrDH9Wkw4fSAgiPXAtKRzUYS9cNqQUNjp/cK0DXovMRTdu3TjK1IRf2EL48zDbMo/i85hf",
"bJn2Cgeva4oAW7ElvxHRfDxlzzHmJk3EFjdSN2FgOnsQ1vEkhIzHtNGFKCBU57ibg9lOH632MyPFHOMj",
"xl8MM27zsbFk2QcIjizGFKFZQeCc08CB4UiR5eGYXIzItCHl0yvQcLxwtdGszh1kLaAzEX8DIc0vtmCb",
"CIHngPNab1KeSQCsuCSfJkUutPmoBOuUvRNzsJGCvzCZBk/5kqs+hH5uWS4Zrzd8a7trZslSwwyA8HCc",
"ESVezuBSjT2pftavuRVvw8fxyRsYZVjQjnQb49DJYROWYDX4SDEfEIWgT06LHDR2dWH7UZH5RM/ZM39/",
"PXv61Au6hpdOGFuwRsn3De5GkgvjtzofcxwFlUM4kMo605RD3t28KSoIhvvkHD/Lc+cEwdE3e3iaH0hV",
"R37SORzxdKVHIrvyStqSmyraKftZd/Qr3EhlLbgS1VWz9jquZu2v0p2jWSSwGHi1We+bMusNCYHznL3E",
"tzonM/gmgGFgmHRZa38Ct2LQH7InkT4JaJReNgLsw4z4GZzLTmBjyraQjdJX+bx45Rl9fl76rn8ptKUl",
"YHIMjlh7jLRcBBRzL7stOR4+TOw8sqOtRUBtrz10Ob6NsAxFqHt6ukLiwhdbpFiCUjp/o0XEYVaxdIEY",
"82ERA7otqaOYcIfClbRsJSuQwaZMVrU4Y7x08kYUiXOVQCRXDorp8TS0H8A1gsswotSemr1AZxb+/5HA",
"zxJaJ9c0DiXVYto5F8mJAVTE17x4h2ek0sIGbcw06sgmAb22lefnUvG6e4pt90JoNXVK2Q/Lm0BW8CKE",
"ZCBAXq6JgI7N5Eci7SfrJ09Dan/y6JsWivTxmwBR+vBlAl36/AVC+tP6oOIPNpa7eITwg1ZLT9hRPLGD",
"HBrPTC4GfpH3W4zPdHIkqtMHAyC4nHcZifkKCChvOEKxmMrf5IwHr+ByxvMVquQAtXod2PDyGkjY01lb",
"jEIqyL2tRrOdTg2jDOOpZlfghhiZr5LUlxmfelZ7TME6M1h4nq5fWFY2xotN9TaUTMprnyQhDuB+Tab5",
"qzZWY7Ql32YiKw54Plpyybh+0HKwEwGNdkyy4GPkw2jfBiLmCmIzMncGhGyEglNKbwqm68oDEdwHd/Nz",
"+PHeiXk2wNmKq7KWYWez1lNzV4tMpvzV7iK/0xsGcYA8rhQMBCu+ZQsNUqhuHOgISaUtukikY+iOYSDp",
"NOV1nsh+1auZzB3di3AeKYI+gEAaD6uhbAPSNN6QvEfmcNC9MKF0uy2436M25f8CaAe9Tgn+k7O+c46T",
"81R0WVt3j7tnuU+J3WOzywFbhOY3effcZjkyOgjeCZ4JLR+041nxPqR0JTFLwZ7nxcHgeADpd6k3apR5",
"LpM8mwP6ktvrTF4it+KKam/sHIM9UQooge+vuwZCtl8LiiXoXKWbfzpUbS1jzK1zJv7X5L8ikeqn18Gk",
"sOa1cM5LhdbKhcJQFLDtQDQvZ37AxniqRzLDz8BSinGJXviGQEHIwvNH3HMuvtD5I/oQYSIPoyxR/Qqi",
"slAXcMOl85wj1hZImFkR/gWWlQoMG90Z2Ez4l41Y6Zt8yYB7lF1T4aBE87bsaGgdtTbZijvofX4p+3S+",
"RDq/U/UzT+pXQ5o7HgTy2L1vuF1Gb4TKWII67mzMKt8Av6bycxpPVhY+BOR9I5p9xzTkllpmYn02CktU",
"ScKhVKlNLJ5pqRYRZ2CzxMhdP2SwZFLtROXRLS1bivoORx/XEGX9zHb6nyKtdBCKK1vxa2HPWnrFD9qC",
"VyGmAQot4ILJr+uuktkHMXzghLboTc8naaetWgqltbpVOotQswue41Xhvw3SOS4WXlw38AcZa7QNlQn9",
"gvPnkSC/8tzzigyMe1aBOwpYluA2J5qguCcy2naJKHAJ+pGWTBeBdMOmG/zg6nA6Kl6ZuIGhSFs82cFE",
"grDe6WB7Mfgqb8S+jGb0cI8nkY/+GQQPsydxCH9E4z/+9O/HBVtzCMwg1JFb1uNKmBWo/tyytz++u9yr",
"chysIjpQSSiw7EpTPMjaeTkfLTkglZJTka+E1/+U5WDz9EChHRfDChB89gQrFU5hWhJkSm6gmgxlifq3",
"j89yAgAyDLybCiZqK1h76xWBvRTBzOSAACPOWn8oyLmxYB8ySu7o6vOaaxp+FCKuKebDNMqGn6/WAoNe",
"QnVJrNiJzj/YIiCoNsyCxoCDDaFNhq5CijgoaHZIjEZ5dbrPjNMFIz7AvwOv6RkusvaeMNtIa4+XC3um",
"nviI7DztKwDS2whi74fewzcR6PjoZQp950UAvf9e9+EP+jmuK18WBIS5PFLuhonGPg9jtY92YGxsCyRA",
"hGLV1Sj5v+PS39UGdvIYztnTGICAKhvGEo2p7zFgKismTlf6SlKq/YHL9fLHlz9OV5jF0RXuU8P1nRht",
"8M3kZ/e7EiaPDrs48dzoX0Uq2LMnC+H81vwc3g1m8lKvt8cdwDgBTwp6vmpBC9+VadSA4BpeQQlDVsQl",
"YXg9h5sIvGG9ZdglN2I/Uq6icfHgtCmm0HCMCVoeCM/hBucBj/gdwsO7ydxkUE1ltKgx9scvWj0zvWCD",
"cpfU3RmKKm8tt3tYYV+m6R7KIaUYyyfZvG6cJ84gtBuMsin1ys8YisRGKfgEZIDkzJNcH6WjrrhfYKhx",
"FP1ar35760Bhv3yYaX2HbPakbFQuk9aI3MKhUOC8W5zOH7I5kj0qJVBBDsiwigs5DH+P3Ig6AI6wtMHt",
"GwiYG+Z74xgPZ5bf+D2NzEfDYZbKOsGhHDgyHwprjjwFLAv+ojpDKagTiZB69LzuXOqVsLGStR+DAkgr",
"9o8JJsjAPyllhuEj+Bwf/WNStInIyLK5TbxMGMPBMF4ak4JDhFsiynpRGjl1Wg1jyKsCmB3aj0ssixOu",
"ZRAjAcX+QNNRC3taTILcC16Ymb6Nco+dJBsVec/om3x2mc57yWcv4tSXfPYNze7fawG45LPXBMMln72L",
"YFzy2c8tJJd8FjKGfqEl/7yXoCIB8fTuSsMJic8VqLZghOESAwx7+XDCK42hjvHuVAGQl/E9P+R/PH/z",
"/fQOWV/5cIZk7uzWEx4vKAOlH8pQY6VHqeTKE8aznKhi9ObgWzs5DTVYff2XWbB0pTN1AG+ldXZvBb8e",
"b8YOGVEM4hiK1CvDmaixkQuP82/oSkPNo0PGdAI8jD+04nz9pJmuttRjYrfEkB/wCNMClEORKtr0BpOZ",
"6KN5YwBtkF6T9aeIcphoIV6S7hUlvGTk2FJwUMawhG+AL2MPk3ao7FQi+Q4DDhzQg11EcdDvK0TOgWOF",
"SkcE7FBG6HQkjywQ5y0ChjbswgvR2fzXsrE5B5uoUwNWm/ux4VBoEsN0Gmsj9F63L71mTA4orujClhaC",
"SAfoeDjMe6xZDjUGjXrC9FMuF3ICZjwue6PBH74FSg7anxTpoQNhXZlNfYE/BFtwKjCCTcMLtUcDpuK8",
"vehAGc8oM/Y6I0BVbZgSCzkkPRLAFBjEs+6AxaRRuDYHFb3gvk3CBYCLQHyU9cgYhCoZJRNfzt3SItfK",
"z9YtgT0ZcYGsyFcYRJIEvm7tzxSw7JZTuaAHy2EfnWpeTFRSqOJqHSpV7LuDdktbfErGehaCIaQNFZm8",
"N2Z6gI/Oqd+Vn3IJJEGggwukjuG9mGuRqEIzuk381OR6A7GfEsi8MsG7sn/rRSEFAcIEVjHErhcHfhGs",
"x6Fid5T2QIrvy47JKj5kpOiFPvEPT+y1XJ+EokcnUBbe07UzjUDD2+IOiiZGzd+pUBrNsG9/3jWZflGg",
"6Qxl3SfbuQ/cDAEkCY77BcZM6F4VaEOoKipld2xHQNannLE/mxD2Ol4PtIIjS2VExwZ2JFiO27djIhgX",
"gBey8huVmIxwozrb0oliHEcA987H6J6pRHvuoIyuV8ug118v3O1gEkSI8x32X9EFa7hKDHdPolpOQa3a",
"XFt0fBzHEKq7hU51QswzkA63wcg7kyha4AyqoHiWtRVuyqA3w0ZaseuvISN3EpIOsIBxqPtKDMWOHmMo",
"UBPYXScKlsDodKx6qHhYQCF4IH6Os7bPWrdJ+4z8Ju2DJDq2fRhdCu2j1O/RPu3GxdLSr4b0qY6FKZid",
"+ugPDtixJJ0/13jYWttsAlpyBvYdZAqlyohN+66ru9su2sFy0FAoWwYINyR99IsDhzg0UiNDzBlew/6v",
"+AvpEvkywffsHzk64JhKq9LCdnHxEQT0uU4sqJPZ1ghQdgw2apw8nT6dPsXqkELxtZycTf4yfTr9CxZN",
"XwLqTvlanvJqJdVp66tbCFByPYpBLnxdTc4m3wp3Ecz/nbqsf3769LP1XaUZMo1XL4S58aIVtKkErttY",
"6M7wEV6GdcxqjdW719pmQiUuHHQuavtiacMaC+702BISTbAzXW2D+dXwDb5/jgEg2zWYMyrhRBkcapiG",
"t3UCS3StGusVePb2h28L9re3r74t2Levv/Gc9Gcxeztlzx1WFvkreyO/RobZxfVPMT98gnQhrPuazEIJ",
"ngGs04WcdxEcvY1tpf4dYsNP/7UWi/t+u1b3/nQjZuu7fts5ICTk9sjw2WcjQ8B8hggvQwhGxTytTf3h",
"+grJPzdchO80Vn756tlfRr/dI+zTD/5/r6uP+45opJm9B/QPRji7m+gxeWTxyJJoiU5oQBEcctrcr+6w",
"XRAWxVfCCeMlkg8Tr/WHPhV4E05wDyd9ai6SNfVX8Esgg7YvDm1/jkWegAMdX52yV16FhX9gd1xJEVfi",
"/Tn2RCQuBz10vF76t4sff5jiP4M6+z237gSenLx+yZ6QtxB/e98Is2Vx2cdQvtOIdc23bToWBP8EiMA1",
"ThdhEfqScBeCLUI/JSOgMsY5bk4tePU/AU9tJxN6k+yXJa9rqBfTdmeKy8BalIpBYye20DSGblsv5Zgw",
"FqN6FXr75LZ2KXgFkhVtbgdTkyJHp8Ph4TQmYLQdMspunz5UUjVlkNYGPqVetTvftVUlfznIdJy4dUjC",
"J9TJu3OSD55bJFH89B6HMxwi9F8OnaHXbTHYnibHvpG1332muDEU5EKlsg0QE0+S0dMuxDdSbHL09b20",
"Ljgyc8TV24JOWMedN/AT9j5oDeOuzrRJwQii2H9Hjy/5P+CW2yGiN9yV6E+Ber/BqCBNdLvbKXsZjApO",
"Y5FfKorbp6LTzuLyQudzKMBtOzVGcG6JFQigvhX2RWbhZQOWx05FIP9qqGUC5keJZV+rGmP9QREnRT2W",
"ygU22qhQBDxHhdR74XX06g8Jm/eXpjo9HkZJcr81lXgRgWqFx72/s6R3B+7k3/73+/Ky0w8exANyYbv8",
"B9TeEhzncQpOxihttSfuYUQtRMv9RC20Wgxh8414SDSCg2oAg17M8ewLhBZCRJMBEn01BOfnP9KJO+gB",
"DvR9kROOLCHnbie2PVUrcToPlcKHKABLiT/gOnGCzEJjmkBKCkd2p+dXlzq6Y7yDago2CUjA4v4hJ6Ud",
"q+23VpDryzDHZ9NdaTnFyecnuAQdj0drg3uQ2AGw1+vDXg8d0vSi+CFN8HKXPEAtws+6gm3hBVeOmUOb",
"pa4xlwmysSBzB9S8kOJk//TsyEtMbSh80gyC/rTiPSiJyS+7RfFCfrWliO/jKXvLIWahvGazLSTgBGrE",
"8J+gxPrheWhCMSRXfy94tVdz68m4ONpnUrMoxbkdasVvMVDur0+fFvvD5n55QHJuO+5mSPonKDVITRzg",
"v09C6Mpfnx4XbF1D/wHKR7VO1nWbzJ7b0WmObkmC3WekBR9stMeGWiw5agbZuGhTe6gzcy/LKA0/Yz/H",
"HgvwalKspt/OZClqzCeyoSSns0yJW8oDydAdVdr7HlXlh2CC3f40o5jhnz/b5JDUQ+UMhnhiB/8pQjdU",
"5FSbiFuoYfApdzQQlNJUkiTLB4fuypZ8KE0HciFnunFsiar9FsJH+iaAuMZrIdYWi/4l4uxKrGbCBJfw",
"QjgoIGmngyzqB41FFh5e/YkdnUZqP4DX6SDGTz/4/5HOgW2dd8Wkl/A8zryzzK92Nww/qR5IIUCY76sQ",
"4PJDgZ07372duuw7xqXnmJ2CeZz+T48HSpL1TAjztDH2FtvL5Ckq1Bt5FKLqsoRxhBUQ0KetNA4Nr4cm",
"eztAUbZc66bYp6mL/r3tmnI6026A3cMw81wg339CTcorESdRndqrjWySKC3USqQFNgz2un7TJQy9Sb7Z",
"Ytxd0oUJb//k1t+CQSwZUrqilQYoSZ0rttI3YGADPwHNXHJLKUAW7gita4brmOWI6UK4tuXUw1BQO/4o",
"yvlqCPOR395ziyl778QIXu2PH2gL8nwygxoqtDUm1XY3lqIXWa0Nxn6mlWd2GMsSKil4uiGZmAq26DnS",
"1FIullhL6z281+F4aVWyLLo8Q3/bllp6eGYesqVG8PHndR2sAtHMQMJ8dxEoqYaRH+gUdJoWPXKgQETa",
"gBQlNgFRn3DEAqpPP9BfB2y9Kb4f6O44sO7umj+3GBexcD9JLofO01iecfAwXlKVs4c/iVACbKQ41boc",
"YQX9BkVfEv6LAY0fT67tVGpp869jCnOMsxoq+Q2hqps08b4IcjNv8/u5azM9YUjpcso8AgU78TBsK8mJ",
"fmSeheQ1zLCwINEjGTTzh5GyQQf1K5T49TxN9ATpUOuYozhMJlJBQkgbbr/DQSEd9QHZJ4y/x0cGzbml",
"ddZLwHNeW9GWHs4kvCodEPF74ri60i3b/c24Tsj2hoiNtHxKQTWngxUafJdkB4wlWYpUO29LRXXSdUOi",
"P5qxY5xp6E/QrePWqXVIH0KdJz9SPkqJGxeTiR+IV8XU1z8eq7qva57KI5x+wD88xYfOZKcfoKvbx3F0",
"H76/E9kXA+Xh/fxHlq177X9wDkiUC83T4G58CrkHGc+6h38MQB3HxgFXJHfZ5nhtnzuo9gHFLRFeaMpi",
"qR6Y7DXRcprN5S2U+9oyuVDa4Ft5z2Xatu+BHJjJFL+N55x6Rg4cJ0Tpo937wQx4+oH+OqDTBCvjA+Jn",
"jIODgH2gazai4n7XbA6np2RWHsdr7gnA0B37HPPnRKc0few9ilfukqtKz+fsyULzumCVKCUZyCHu0D/h",
"FSRmFmjBjcFtBbZMPJ6yn2M9wVCGk0oEB1eUtNGmjk49DtdHr0kq1lP0Ny8kcUAXHLra44UewCVBoHun",
"5/QIRP8g+T6ufy5AD2hKqsJNH+tWzJIoZC6bZv3bEOmrkPNL/QTQv7uzka8DkF/IVkakwT5WopY3EOL5",
"m+5kx73/2Bt5EXR9kAyBwvHUStv6oWNDxpYzeOYUfASkDbSsaqfeby9MACs3U3Y/h35hSdguNdwJgQOh",
"tSBYKnjrzhsguQuhqrZ93x8yoADQv9xhVF+asJ/0wzwgx0SL0kNZFvZoTIm29LklF1z5/cSWLvJOy7bO",
"Y9b+86qt3NFtXhothU9i/ZciljhsFP6FmaMKuub45369ooTwoelCOlAUxDGDKmJ+sKJbNfIORSMPmymx",
"Yz2wJOhjgPElSUNbvVGhATj4t5nTTQklRSCJKtRrLNpGspIUqaSUIxVCPLwQTICDiplDhS8D5PvKXlI/",
"ZyfW2J6oycpFdBTacn89GhvoBux0WAbF3bRVHrsLKpht/Gb4fQQnduDZIYxwKXg1ZT9ox3hd601AS1hM",
"VHz7yTB+zyZ3Ur2fpxhSZ+Q+DlTT1hKUqrt3zHBybfPQKPSmWzMpC2DoybxXGy+yBWvDt2ddJGd3G4tg",
"xH/h/hzFMvPddqZaieNBgJ1Yf2pq2Sea4YkIM2zzrTAnYCv2ysiXlgXy2IxZn/DGLalL1qBtHuw3sW1e",
"oF1Mz6RMzFaA6jDwtgYpewIxHEnt/fAJ8KAlryCLH6J/MLmplYBZoyoMCJEGajodg8UfjP3EJ6TpG2Of",
"h5Jp2xRaz5WpRj70IUHgF9LFCoOMsxcBLaI68Z8bLut8Qum3wr3QzwmFj+LS58bJUq65cmP9ie0mf/mS",
"QiXn872k2Cg5l6KC8nih0Dec5rZ5AFx+cN1FH+OiczVNh66ul372ccHlWIfvTtbbbGA51ln+5HFoQz55",
"HF1XoW74XnXt4fIzZC1gG4ac49yVyz82066kGTwiPyrBKmlE6TSELeWE6SK+IYUNCSNuid3tPbhT9ko5",
"+NH/qwIpGqLsajF3TDfunEW5umKC3uXQxQRLTqK1DdrjeUbsUYGCUC34DenTLThuaXSzWLLpFJOs7XZV",
"S3VdgJKOLrU98uZLaQ7Jmhc1t8sTK/xLDlWEmqNTTneLqhutXah162HpdiIORYf3nMDf5tS8lOZ7ad2e",
"6hiRKKCtCuzYH/0UQb2vcWat+8w7ZNP6Vt50I2NQHPIXWSUqv/+iQiOJ02xjpBOtZkHxiEXb1Iraf1FN",
"c9s3gkHWFhRDSo3j/X4mx3Bedawkxy2zGiuY99vSkSU+FDcjkdD/wCuqCLsSXIEH/JxVRq8xxQjyQ0Ag",
"Ow9Bvn37PlvqjbjBdtY4C5bBtlmLALTkQvU81I+mRhSR+bQfJt0P2WU7GEYDFDHM039IJUiMWHGp+i79",
"tJVT7JPEayN4taUeRHEmEmtt8P9TRyx7zrSKjVAgH4+at7O5FzOBOyFHTRu7Z/gfVazLm5/+/Gjmp4Iy",
"x0KHb1xTtdshcfrbmO7GJFb2CtWAjZczK973czowobGby1hzigiGREdO3+fzOTwy75LE+BlLxUBn1nwO",
"47OnD57EOEq5AcyMUWswsxE7KtOmJamNT4+/fEXH6yyDJPmcGbFoam5QtcnbRKfsUtw6lJGoBnGzxqI1",
"Vv4qEDvnDItooV+V3vSMmzbuwaUzL89/XvHsgAB2pxv6QdWYFzTQAO/0O/LHFsGgANzwIYg1Hf0ZiKHC",
"O8qMF85jUw34Ais24sfcOV4uV1inDCgds5l13Tio3jNEa+d0DRhhdX0D3QWR8MG4G8pBSmVlJbqV73s1",
"IguoEAkgXfyfbwtW+vsRdC1x64SC2JPh8/N6ha7CMdWkPv8B+L0WFbQ3iz/drurfvCahJ91Qk/BeZ/0v",
"f0zOAIrC46tmkIqaMJmkw14sWqDVXJqVV9H8YzKcJLm8EHFE8YqgFtEwJ1J1O91IdSOp3Y/TEKtHNbyx",
"73EuAoopT4Z1vS0w6RVbEQ8oSNAl2GuVwc+Gvc3ajmW7a9TqgJszWMV7RnWyoufN5sCg0eDeMUe1Zmls",
"pIz14aGggFx4ZM25dSdzbTY81LVpASuXorz2mIR+uLQLqbqY0xMzeiGWKY+hIns6b6caX9uPJVQ+CE7L",
"J73m39AwWrjjNKaciqy3I0jlZ1kYYW1/1rTl+9GOdp+rZYA7KS2by9tQyhgKZrStt2F50P/X7sSsD3bD",
"jtAGdRcaiofSn1AZAf2EBUUBhm7VWnl6haOBT9pkIMKpqHZaWvfeQ+yeJX2to93EQ2SvsbEzwejYE4rh",
"kXOqqRdKghwXEaUBPkREPKdVKMWXQhl2JUz93avnL0OCilBVcESENJXwGhVdyTTIJLpoN0Ud52wNLcZ6",
"2wJVN1y6HcH40cPklL2SRNOwR63Q3jdpZEwVIYRAbwaqo5jFp+ZT5XN+IT+m30blYE/6Tht6jzP/6hns",
"DDYa41Sk+7//mZVLbnjpLxaPd7w50M6LeALrT7a1Gfy+C4D/AB3oxDSPzwJAWtHlMcRYp7RZZ4GC4weZ",
"03COKeydQ5a2t/aATEI395F9Fmgn/cre4OfJk7c00sd8nvXjRZfts0Stdvr9a4Ms6vcQXnYaRITHl3li",
"QHfHGh2Z6T6ybek0CZYqEo2Jqy0ITLUsnaWAyoVQwEPUgtGf/uDJ2t9FFzR1Ak4aZg53UUz/HDJmH7zS",
"BlnZ6988Aphwng/l/sJJeB26x+4ztK61cRlyE7drbYNwiv+48q8WiUuS41VshbkhAZTL2pYc6MPciE7I",
"v/+YqQZqN+k5SMtHli21dQXTN8Kw7y4v314UfaGoFUi0SXwGAxXp3rb9ch+jlgPl+oxNIkfYvnxL6Biy",
"if4aeLWAK30tTBIu2e/QtLtb7x5xs97daa9+N1uVKZS1i+cvvlxVh/MMFO/6crbiUGqE6i5kKICayh5W",
"qGTjpb5au27SUz5nAVMJ0hyZz5+1QKP/NtUTxtzMifv893EdB9Y4irHGl/sWhXDr8mBFIhW3pSvrleyk",
"9eZGGBFD9K+FJzHnVU576i/hHJSnH9THcRfxOUXhbIJ/Ppay5W7okr6IeHgMfhRmG106L0D3O2D+AVS/",
"X6dzba4fTFkp8qUe756dfqBMRKg3QDELuRI1IIK33qck7B8PRBE70WDbhg4vBuutJzCpGrJLJgNQbTqt",
"boSxaAjGzAHrtI71T7rB+dj2mvJIwjR+L47R+pewqaOUq4eeQJ0YaayDC+aifYEuYCCrYltMr3oFmEIY",
"kKGic7SmMD53rC0anS/K84021xdtfsP/LztxmLEnpfke1FwwqrRDvnxemgHQ1tGTwlKIghXvp+w51v0m",
"GqmT/jvBphnJKjQZVxoumXPGsRSfgdcF9G4nvEwNZEbpjWrdFFjyckfn5+Y6KV/4UJJNMsOj13S8bymH",
"Hsk5YVZS8XpPwMrPYnahy2vhyOOOBvmUDmiMgnEG7cyZXYq6xoqdqT8/CW0sdW39HsKNL+fYECcx5DCs",
"3wBdJJGzoYi7CdQZJiXKLIVy9Zatjb8lKvIagYG7ceuGhGFsUVwwXtdQlB7jZ0IaNM5Z1hL5PAjUir7t",
"vUoGLyt/RfcClC+NPy51XWGF80sC8kL+2rr9BKFH3MrgWMR1krsCOG+pK8G+evr0aXRq+NcZdv2M0ZuC",
"270hBmH+cVEGfk/2spK9YWQDsWl+g+8/aD+O4RneGz19CbphtGTZEuzaaKdLXf+xg3EaRS2bDxZe76uW",
"2H0fbgTwOw1ZiystrDpy2eybn1RsGP1wxZHDHEMFkun3cNtM/5PtcKiRuFchjIUUowQ4N/rXGGAOZukS",
"BGgvEcZEV89Jp+x77rUxUUmy9mMVeZsqjTB2pdWRIzQPyIXEm0Kz6ofOr4/zDAXyeCSouIIvX1sLkJ6a",
"Ru23yIfIg/4mmUYlvh1Ra7WwzOmzXsP+HqG0agKEfGBzfRDPoDM9RdibbSjAzGPzeejLVrTxKaGJOSln",
"eEUnqZtBTvC62QAFhV191zxolal0miETb6Ow7RLg+gskn7Dt+227P8e3HsOY0u9UP8KmApUkIg0fYQYb",
"8aL4XutVSmXeiIHTD/63EW0whpnTnjYYDIoOI+Uja5UOeo9QFnOp19t70cdQeZL7sdAt74dPjgp4TDA/",
"l7U4g6MfIHjZMg1u2X88f/M9XCFcJVG6lHIYyuw8VNcQ/7/PoQ8HWxIEEq9rXibhZ2k9YkSH132GkQG5",
"Wn+7+PGHYMuBGqE2xq94Dv3Mi6/Pnj7txK8kFBU0Z6SsDiR9GsuwzLdNl1rGaMOjCOXxKiLu8Iy9nd56",
"9/mdKrl//Pj/AgAA//8=",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+19
View File
@@ -2,6 +2,7 @@ package server
import (
"errors"
"math"
"net/http"
"strings"
@@ -17,6 +18,24 @@ func (s *Server) ListLeadSessions(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, sessions)
}
func (s *Server) ListLeadEvents(w http.ResponseWriter, r *http.Request, params api.ListLeadEventsParams) {
before := int64(math.MaxInt64)
if params.Before != nil {
before = *params.Before
}
turns := 50
if params.Turns != nil {
turns = *params.Turns
}
page, err := s.store.LeadEventsPage(r.Context(), actor(r), before, turns)
if err != nil {
writeStoreError(w, err)
return
}
writeJSON(w, http.StatusOK, page)
}
func (s *Server) MessageLead(w http.ResponseWriter, r *http.Request) {
req := api.MessageCreate{}
if !readJSON(w, r, &req) {
+27
View File
@@ -1297,6 +1297,33 @@ func (s *Store) LeadEvents(ctx context.Context, owner string, after int64, limit
return scanEvents(rows)
}
func (s *Store) LeadEventsPage(ctx context.Context, owner string, before int64, turns int) (api.EventPage, error) {
from := int64(0)
more := true
err := s.db.QueryRowContext(ctx, `
SELECT seq FROM events WHERE owner = ? AND agent_session_id IS NOT NULL AND kind = 'turn.ended' AND seq < ?
ORDER BY seq DESC LIMIT 1 OFFSET ?
`, owner, before, turns).Scan(&from)
if errors.Is(err, sql.ErrNoRows) {
more = false
} else if err != nil {
return api.EventPage{}, err
}
rows, err := s.db.QueryContext(ctx, `
SELECT `+eventColumns+`
FROM events WHERE owner = ? AND agent_session_id IS NOT NULL AND seq > ? AND seq < ? ORDER BY seq
`, owner, from, before)
if err != nil {
return api.EventPage{}, err
}
events, err := scanEvents(rows)
if err != nil {
return api.EventPage{}, err
}
return api.EventPage{Events: events, More: more}, nil
}
func scanEvents(rows *sql.Rows) ([]api.Event, error) {
defer rows.Close()
+1 -1
View File
@@ -58,7 +58,7 @@ async function main() {
if (/^#\/(task|project)\/\w+(\/\w+)?$/.test(linked)) {
location.hash = linked;
}
startLeadStream();
void startLeadStream();
replace(document.body, banner, h("div", { class: "panes" }, leadPane(), taskPane()));
bindShortcuts();
identityBanner(banner);
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
+2 -1
View File
@@ -1,6 +1,6 @@
import { post } from "./api.js";
import { h } from "./dom.js";
import { currentLead, leadLoading, state } from "./state.js";
import { currentLead, leadHasOlder, leadLoading, loadOlderLead, state } from "./state.js";
import { compactButton, mountChat } from "./task.js";
const leadSource = {
key: () => "lead",
@@ -14,6 +14,7 @@ const leadSource = {
transcriptTopic: "lead.transcript",
streamTopic: "lead.stream",
readyTopic: "lead.ready",
older: { has: leadHasOlder, load: loadOlderLead },
empty: "",
canSend: () => true,
placeholder: "Message your lead. Paste or drop images to attach them.",
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"lead.js","sourceRoot":"","sources":["../src/lead.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,UAAU,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,SAAS,EAA8B,MAAM,WAAW,CAAC;AAEjF,MAAM,UAAU,GAAe;IAC7B,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM;IACjB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc;IAClC,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,WAAW;IACpB,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS;IACvB,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,CAAC,eAAe,CAAC;IAChC,eAAe,EAAE,iBAAiB;IAClC,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;IACnB,WAAW,EAAE,yDAAyD;IACtE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE;QAC7D,MAAM,IAAI,CAAC,uBAAuB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACzG,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ;IACf,OAAO;QACL,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7H,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAiB;IACzC,OAAO,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
{"version":3,"file":"lead.js","sourceRoot":"","sources":["../src/lead.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,UAAU,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,SAAS,EAA8B,MAAM,WAAW,CAAC;AAEjF,MAAM,UAAU,GAAe;IAC7B,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM;IACjB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc;IAClC,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,WAAW;IACpB,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS;IACvB,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,CAAC,eAAe,CAAC;IAChC,eAAe,EAAE,iBAAiB;IAClC,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,KAAK,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE;IACjD,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;IACnB,WAAW,EAAE,yDAAyD;IACtE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE;QAC7D,MAAM,IAAI,CAAC,uBAAuB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACzG,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ;IACf,OAAO;QACL,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAChB,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7H,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAiB;IACzC,OAAO,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
+30 -2
View File
@@ -366,8 +366,36 @@ export function selectTask(taskId) {
dropClosedFeeds();
notify("selection");
}
export function startLeadStream() {
subscribe(0, { lead: true }, leadFeed.push, () => undefined, () => leadFeed.load([]));
const leadPage = 50;
let leadMore = false;
async function fetchLeadTail() {
for (;;) {
try {
return await get("/api/me/lead/events", { query: { turns: leadPage } });
}
catch (err) {
console.error("load lead history", err);
await new Promise((resolve) => setTimeout(resolve, 2_000));
}
}
}
export async function startLeadStream() {
const page = await fetchLeadTail();
leadMore = page.more;
subscribe(page.events.at(-1)?.seq ?? 0, { lead: true }, leadFeed.push, () => undefined, () => leadFeed.load(page.events));
}
export function leadHasOlder() {
return leadFeed.ready && leadMore;
}
export async function loadOlderLead() {
const first = leadFeed.events[0];
if (!first) {
return [];
}
const page = await get("/api/me/lead/events", { query: { before: first.seq, turns: leadPage } });
leadMore = page.more;
leadFeed.events.unshift(...page.events);
return page.events;
}
export function handleEvent(ev) {
if (ev.task_id) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+73 -6
View File
@@ -210,11 +210,17 @@ export function mountChat(root, src, makeHead) {
const headImpl = makeHead(() => renderHead());
const scanner = h("div", { class: "scanner", hidden: true });
const loading = h("div", { class: "empty" }, "loading…");
const earlier = h("div");
let sink;
const setRunning = (running) => {
transcript.classList.toggle("running", running);
scanner.hidden = !running;
};
const putBefore = (el, anchor) => {
if (sink) {
sink.append(el);
return;
}
if (anchor?.parentNode === transcript) {
transcript.insertBefore(el, anchor);
}
@@ -394,9 +400,8 @@ export function mountChat(root, src, makeHead) {
}
return handoff.summary;
};
const appendEvents = (events) => {
const renderEvents = (events) => {
let contextChanged = false;
headImpl.onEvents?.(events);
for (const ev of events) {
const p = ev.payload;
const parent = parentOf(p);
@@ -411,12 +416,15 @@ export function mountChat(root, src, makeHead) {
claudeTaskStatus(p, ctx);
continue;
}
if ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message") {
if (!sink && ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message")) {
dropStream();
}
if (ev.kind === "user.message" && ev.actor !== "byre") {
if (!sink && ev.kind === "user.message" && ev.actor !== "byre") {
confirmPending(String(p["text"] ?? ""));
}
if (ev.kind === "session.context" && sink) {
continue;
}
if (ev.kind === "session.context" && ev.agent_session_id) {
const prev = context.get(ev.agent_session_id);
const window = Number(p["window"]) || prev?.window || 0;
@@ -501,6 +509,11 @@ export function mountChat(root, src, makeHead) {
}
put(el);
}
return contextChanged;
};
const appendEvents = (events) => {
headImpl.onEvents?.(events);
const contextChanged = renderEvents(events);
syncRunning();
if (contextChanged) {
renderHead();
@@ -513,6 +526,54 @@ export function mountChat(root, src, makeHead) {
pin();
jump();
};
const prependEvents = (events) => {
const live = ctx;
const fromBottom = transcript.scrollHeight - transcript.scrollTop;
const box = document.createDocumentFragment();
ctx = newCtx();
sink = box;
renderEvents(events);
sink = undefined;
ctx = live;
syncRunning();
fillTodoLinks(box, models[0]);
earlier.after(box);
transcript.scrollTop = transcript.scrollHeight - fromBottom;
};
let disposed = false;
let fetching = false;
const loadOlder = async () => {
const older = src.older;
if (!older || fetching || !older.has()) {
return;
}
fetching = true;
replace(earlier, h("div", { class: "empty" }, "loading earlier…"));
try {
const events = await older.load();
replace(earlier);
if (!disposed) {
prependEvents(events);
}
}
finally {
fetching = false;
replace(earlier);
}
watchTop();
};
const top = new IntersectionObserver((entries) => {
if (entries.some((e) => e.isIntersecting)) {
loadOlder().catch((err) => console.error("load earlier events", err));
}
}, { root: transcript, rootMargin: "400px 0px 0px 0px" });
const watchTop = () => {
if (disposed) {
return;
}
top.unobserve(earlier);
top.observe(earlier);
};
const renderAll = () => {
stuck = true;
dropStream();
@@ -529,12 +590,13 @@ export function mountChat(root, src, makeHead) {
replace(transcript, h("div", { class: "empty" }, src.empty), scanner);
return;
}
replace(transcript, scanner);
replace(transcript, earlier, scanner);
if (src.loading()) {
transcript.prepend(loading);
}
appendEvents(src.events());
renderComposer();
watchTop();
};
const offs = [
onEach(src.resetTopics, renderAll),
@@ -544,7 +606,10 @@ export function mountChat(root, src, makeHead) {
}),
onEach([src.transcriptTopic], (fresh) => appendEvents(fresh)),
onEach([src.streamTopic], (ev) => appendDelta(ev)),
onEach([src.readyTopic], () => loading.remove()),
onEach([src.readyTopic], () => {
loading.remove();
watchTop();
}),
onEach(["tasks", "sessions"], () => fillTodoLinks(transcript, models[0])),
on("jump", jump),
];
@@ -554,6 +619,8 @@ export function mountChat(root, src, makeHead) {
for (const off of offs) {
off();
}
disposed = true;
top.disconnect();
headImpl.dispose?.();
resizes.disconnect();
mutations.disconnect();
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"todolink.js","sourceRoot":"","sources":["../src/todolink.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"todolink.js","sourceRoot":"","sources":["../src/todolink.ts"],"names":[],"mappings":"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"}
+45
View File
@@ -54,6 +54,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/me/lead/events": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** @description The calling user's lead events, oldest first, a page of whole turns: those after the turns+1'th turn.ended before the before seq (or before the latest event when before is absent). Page back by passing the first event's seq as before. */
get: operations["listLeadEvents"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/me/lead/messages": {
parameters: {
query?: never;
@@ -1374,6 +1391,11 @@ export interface components {
/** Format: date-time */
created_at: string;
};
EventPage: {
events: components["schemas"]["Event"][];
/** @description Whether there are earlier events than this page. */
more: boolean;
};
Focus: {
/** @description The project the right pane shows; the task's project when task_id is set */
project_id?: string;
@@ -1595,6 +1617,29 @@ export interface operations {
};
};
};
listLeadEvents: {
parameters: {
query?: {
before?: number;
turns?: number;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Up to turns turns (default 50), plus any turn still running when before is absent. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["EventPage"];
};
};
};
};
messageLead: {
parameters: {
query?: never;
+1 -1
View File
@@ -60,7 +60,7 @@ async function main(): Promise<void> {
if (/^#\/(task|project)\/\w+(\/\w+)?$/.test(linked)) {
location.hash = linked;
}
startLeadStream();
void startLeadStream();
replace(document.body, banner, h("div", { class: "panes" }, leadPane(), taskPane()));
bindShortcuts();
+2 -1
View File
@@ -1,6 +1,6 @@
import { post } from "./api.js";
import { h } from "./dom.js";
import { currentLead, leadLoading, state } from "./state.js";
import { currentLead, leadHasOlder, leadLoading, loadOlderLead, state } from "./state.js";
import { compactButton, mountChat, type ChatSource, type Head } from "./task.js";
const leadSource: ChatSource = {
@@ -15,6 +15,7 @@ const leadSource: ChatSource = {
transcriptTopic: "lead.transcript",
streamTopic: "lead.stream",
readyTopic: "lead.ready",
older: { has: leadHasOlder, load: loadOlderLead },
empty: "",
canSend: () => true,
placeholder: "Message your lead. Paste or drop images to attach them.",
+34 -3
View File
@@ -1,4 +1,4 @@
import { get, put, subscribe, type AgentSession, type ByreEvent, type Focus, type InboxItem, type Project, type Snapshot, type Task, type TaskTab, type User } from "./api.js";
import { get, put, subscribe, type AgentSession, type ByreEvent, type Focus, type InboxItem, type Project, type Schemas, type Snapshot, type Task, type TaskTab, type User } from "./api.js";
import { notifyInbox, notifyTurn } from "./notifications.js";
export type Topic =
@@ -441,8 +441,39 @@ export function selectTask(taskId: string | undefined): void {
notify("selection");
}
export function startLeadStream(): void {
subscribe(0, { lead: true }, leadFeed.push, () => undefined, () => leadFeed.load([]));
const leadPage = 50;
let leadMore = false;
async function fetchLeadTail(): Promise<Schemas["EventPage"]> {
for (;;) {
try {
return await get("/api/me/lead/events", { query: { turns: leadPage } });
} catch (err) {
console.error("load lead history", err);
await new Promise((resolve) => setTimeout(resolve, 2_000));
}
}
}
export async function startLeadStream(): Promise<void> {
const page = await fetchLeadTail();
leadMore = page.more;
subscribe(page.events.at(-1)?.seq ?? 0, { lead: true }, leadFeed.push, () => undefined, () => leadFeed.load(page.events));
}
export function leadHasOlder(): boolean {
return leadFeed.ready && leadMore;
}
export async function loadOlderLead(): Promise<ByreEvent[]> {
const first = leadFeed.events[0];
if (!first) {
return [];
}
const page = await get("/api/me/lead/events", { query: { before: first.seq, turns: leadPage } });
leadMore = page.more;
leadFeed.events.unshift(...page.events);
return page.events;
}
export function handleEvent(ev: ByreEvent): void {
+79 -6
View File
@@ -24,6 +24,7 @@ export type ChatSource = {
transcriptTopic: Topic;
streamTopic: Topic;
readyTopic: Topic;
older?: { has: () => boolean; load: () => Promise<ByreEvent[]> };
empty: string;
canSend: (sessionState: string) => boolean;
placeholder: string;
@@ -287,11 +288,17 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
const scanner = h("div", { class: "scanner", hidden: true });
const loading = h("div", { class: "empty" }, "loading…");
const earlier = h("div");
let sink: DocumentFragment | undefined;
const setRunning = (running: boolean) => {
transcript.classList.toggle("running", running);
scanner.hidden = !running;
};
const putBefore = (el: HTMLElement, anchor: HTMLElement | undefined) => {
if (sink) {
sink.append(el);
return;
}
if (anchor?.parentNode === transcript) {
transcript.insertBefore(el, anchor);
} else {
@@ -483,9 +490,8 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
return handoff.summary;
};
const appendEvents = (events: ByreEvent[]) => {
const renderEvents = (events: ByreEvent[]): boolean => {
let contextChanged = false;
headImpl.onEvents?.(events);
for (const ev of events) {
const p = ev.payload as Record<string, unknown>;
const parent = parentOf(p);
@@ -500,12 +506,15 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
claudeTaskStatus(p, ctx);
continue;
}
if ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message") {
if (!sink && ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message")) {
dropStream();
}
if (ev.kind === "user.message" && ev.actor !== "byre") {
if (!sink && ev.kind === "user.message" && ev.actor !== "byre") {
confirmPending(String(p["text"] ?? ""));
}
if (ev.kind === "session.context" && sink) {
continue;
}
if (ev.kind === "session.context" && ev.agent_session_id) {
const prev = context.get(ev.agent_session_id);
const window = Number(p["window"]) || prev?.window || 0;
@@ -590,6 +599,12 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
}
put(el);
}
return contextChanged;
};
const appendEvents = (events: ByreEvent[]) => {
headImpl.onEvents?.(events);
const contextChanged = renderEvents(events);
syncRunning();
if (contextChanged) {
renderHead();
@@ -603,6 +618,58 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
jump();
};
const prependEvents = (events: ByreEvent[]) => {
const live = ctx;
const fromBottom = transcript.scrollHeight - transcript.scrollTop;
const box = document.createDocumentFragment();
ctx = newCtx();
sink = box;
renderEvents(events);
sink = undefined;
ctx = live;
syncRunning();
fillTodoLinks(box, models[0]!);
earlier.after(box);
transcript.scrollTop = transcript.scrollHeight - fromBottom;
};
let disposed = false;
let fetching = false;
const loadOlder = async () => {
const older = src.older;
if (!older || fetching || !older.has()) {
return;
}
fetching = true;
replace(earlier, h("div", { class: "empty" }, "loading earlier…"));
try {
const events = await older.load();
replace(earlier);
if (!disposed) {
prependEvents(events);
}
} finally {
fetching = false;
replace(earlier);
}
watchTop();
};
const top = new IntersectionObserver(
(entries) => {
if (entries.some((e) => e.isIntersecting)) {
loadOlder().catch((err: unknown) => console.error("load earlier events", err));
}
},
{ root: transcript, rootMargin: "400px 0px 0px 0px" },
);
const watchTop = () => {
if (disposed) {
return;
}
top.unobserve(earlier);
top.observe(earlier);
};
const renderAll = () => {
stuck = true;
dropStream();
@@ -619,12 +686,13 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
replace(transcript, h("div", { class: "empty" }, src.empty), scanner);
return;
}
replace(transcript, scanner);
replace(transcript, earlier, scanner);
if (src.loading()) {
transcript.prepend(loading);
}
appendEvents(src.events());
renderComposer();
watchTop();
};
const offs = [
@@ -635,7 +703,10 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
}),
onEach([src.transcriptTopic], (fresh) => appendEvents(fresh as ByreEvent[])),
onEach([src.streamTopic], (ev) => appendDelta(ev as ByreEvent)),
onEach([src.readyTopic], () => loading.remove()),
onEach([src.readyTopic], () => {
loading.remove();
watchTop();
}),
onEach(["tasks", "sessions"], () => fillTodoLinks(transcript, models[0]!)),
on("jump", jump),
];
@@ -646,6 +717,8 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
for (const off of offs) {
off();
}
disposed = true;
top.disconnect();
headImpl.dispose?.();
resizes.disconnect();
mutations.disconnect();
+1 -1
View File
@@ -9,7 +9,7 @@ export function todoLink(projectId: string, title: string, children: Node[]): HT
return h("span", { class: "todo-link", data: { project: projectId, title } }, h("span", { class: "todo-title" }, ...children), " ", h("span", { class: "todo-action" }));
}
export function fillTodoLinks(root: HTMLElement, model: string): void {
export function fillTodoLinks(root: ParentNode, model: string): void {
for (const el of root.querySelectorAll<HTMLElement>(".todo-link")) {
fill(el, model);
}