@intentface/latch-drizzle 0.9.1 → 0.10.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AA2I7C;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC,CAef;AA2CD,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3D"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AA6N7C;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC,CAgBf;AA2CD,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3D"}
package/dist/migrate.js CHANGED
@@ -58,6 +58,32 @@ const DDL = [
58
58
  max_steps INTEGER,
59
59
  scope TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
60
60
  `CREATE UNIQUE INDEX IF NOT EXISTS latch_agents_owner_name ON latch_agents (owner, name)`,
61
+ // Append-only publish history for `latch_agents`. The live row is always a
62
+ // copy of the version its `published_version` names; rollback appends a NEW
63
+ // version carrying an old record, so history is never rewritten.
64
+ `CREATE TABLE IF NOT EXISTS latch_agent_versions (
65
+ id TEXT PRIMARY KEY, owner TEXT NOT NULL, name TEXT NOT NULL, version INTEGER NOT NULL,
66
+ record TEXT NOT NULL, author TEXT, source TEXT,
67
+ changed_fields TEXT, change_message TEXT, created_at INTEGER NOT NULL)`,
68
+ // Backstops the max(version)+1 race: a losing concurrent publish retries.
69
+ `CREATE UNIQUE INDEX IF NOT EXISTS latch_agent_versions_owner_name_version
70
+ ON latch_agent_versions (owner, name, version)`,
71
+ `CREATE INDEX IF NOT EXISTS latch_agent_versions_owner_name_idx
72
+ ON latch_agent_versions (owner, name)`,
73
+ // Who made which version live, and when. Activating an existing version
74
+ // appends no version, so without this a rollback leaves no trace.
75
+ `CREATE TABLE IF NOT EXISTS latch_agent_activations (
76
+ id TEXT PRIMARY KEY, owner TEXT NOT NULL, name TEXT NOT NULL, version INTEGER NOT NULL,
77
+ actor TEXT, source TEXT, created_at INTEGER NOT NULL)`,
78
+ `CREATE INDEX IF NOT EXISTS latch_agent_activations_owner_name_idx
79
+ ON latch_agent_activations (owner, name, created_at)`,
80
+ // One unpublished working copy per (agent, editor) — per PERSON, so two org
81
+ // admins can't overwrite each other's half-written prompt.
82
+ `CREATE TABLE IF NOT EXISTS latch_agent_drafts (
83
+ owner TEXT NOT NULL, name TEXT NOT NULL, editor TEXT NOT NULL,
84
+ record TEXT NOT NULL, base_version INTEGER, updated_at INTEGER NOT NULL,
85
+ PRIMARY KEY (owner, name, editor))`,
86
+ `CREATE INDEX IF NOT EXISTS latch_agent_drafts_owner_idx ON latch_agent_drafts (owner)`,
61
87
  `CREATE TABLE IF NOT EXISTS latch_schedules (
62
88
  id TEXT PRIMARY KEY, owner TEXT NOT NULL, agent TEXT NOT NULL, identity TEXT,
63
89
  cron TEXT, timezone TEXT, prompt TEXT NOT NULL, delivery TEXT,
@@ -113,6 +139,10 @@ const ALTERS = [
113
139
  `ALTER TABLE latch_chats ADD COLUMN kind TEXT NOT NULL DEFAULT 'user'`,
114
140
  `ALTER TABLE latch_memory_chat_state ADD COLUMN last_extracted_key TEXT`,
115
141
  `ALTER TABLE latch_agents ADD COLUMN attached_evals TEXT`,
142
+ `ALTER TABLE latch_agents ADD COLUMN published_version INTEGER`,
143
+ `ALTER TABLE latch_runs ADD COLUMN agent_version INTEGER`,
144
+ `ALTER TABLE latch_agent_versions ADD COLUMN changed_fields TEXT`,
145
+ `ALTER TABLE latch_agent_versions ADD COLUMN change_message TEXT`,
116
146
  // AFTER the kind ALTER above — a pre-kind DB can't index the column earlier.
117
147
  `CREATE INDEX IF NOT EXISTS latch_chats_sweep_idx ON latch_chats (kind, updated_at)`,
118
148
  ];
@@ -133,6 +163,57 @@ function isColumnExists(error) {
133
163
  }
134
164
  return false;
135
165
  }
166
+ /**
167
+ * Data backfills. Run AFTER `ALTERS` (they reference columns the ALTERs add)
168
+ * and written to be idempotent, so every `migrate()` can re-run them safely.
169
+ */
170
+ const BACKFILL = [
171
+ // Every pre-existing agent becomes v1, so the prompt that was already live is
172
+ // a rollback target. Without this the FIRST publish would create v1 from the
173
+ // NEW content and the running prompt would be lost for good — the exact
174
+ // failure version history exists to prevent. `WHERE NOT EXISTS` makes it a
175
+ // no-op once an agent has any version.
176
+ //
177
+ // `json(col)` embeds the JSON columns as JSON rather than as a quoted string.
178
+ // `max_steps` is stored column-encoded (NULL=default, 0=unlimited, N=cap) and
179
+ // is decoded here to its `AgentRecord` form.
180
+ `INSERT INTO latch_agent_versions (id, owner, name, version, record, author, source, created_at)
181
+ SELECT 'agv_' || lower(hex(randomblob(10))), a.owner, a.name, 1,
182
+ json_object(
183
+ 'name', a.name,
184
+ 'title', a.title,
185
+ 'description', a.description,
186
+ 'modelId', a.model_id,
187
+ 'instructions', a.instructions,
188
+ 'connections', json(a.connections),
189
+ 'tools', json(a.tools),
190
+ 'exec', json(a.exec),
191
+ 'subagents', json(a.subagents),
192
+ 'attachedEvals', json(a.attached_evals),
193
+ 'maxSteps', CASE WHEN a.max_steps IS NULL THEN NULL
194
+ WHEN a.max_steps = 0 THEN 'unlimited'
195
+ ELSE a.max_steps END,
196
+ 'scope', a.scope),
197
+ NULL, 'backfill', a.created_at
198
+ FROM latch_agents a
199
+ WHERE NOT EXISTS (SELECT 1 FROM latch_agent_versions v
200
+ WHERE v.owner = a.owner AND v.name = a.name)`,
201
+ // Point every live row at its version. Only fills NULLs, so a row that has
202
+ // published since the backfill keeps its real version.
203
+ `UPDATE latch_agents SET published_version = (
204
+ SELECT MAX(v.version) FROM latch_agent_versions v
205
+ WHERE v.owner = latch_agents.owner AND v.name = latch_agents.name)
206
+ WHERE published_version IS NULL`,
207
+ // ...and record that activation, so the publish timeline is complete from the
208
+ // first row rather than starting at whenever this feature shipped.
209
+ `INSERT INTO latch_agent_activations (id, owner, name, version, actor, source, created_at)
210
+ SELECT 'aga_' || lower(hex(randomblob(10))), a.owner, a.name, a.published_version,
211
+ NULL, 'backfill', a.created_at
212
+ FROM latch_agents a
213
+ WHERE a.published_version IS NOT NULL
214
+ AND NOT EXISTS (SELECT 1 FROM latch_agent_activations x
215
+ WHERE x.owner = a.owner AND x.name = a.name)`,
216
+ ];
136
217
  /**
137
218
  * Run the schema against any executor — a driver-agnostic seam so a consumer on
138
219
  * a non-libsql driver (e.g. better-sqlite3's `db.run`) can create the tables
@@ -157,6 +238,8 @@ export async function migrateWith(exec) {
157
238
  throw error;
158
239
  }
159
240
  }
241
+ for (const stmt of BACKFILL)
242
+ await exec(stmt);
160
243
  }
161
244
  /**
162
245
  * One-off upgrade for DBs created before `latch_messages` moved to a composite
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.pg.d.ts","sourceRoot":"","sources":["../src/migrate.pg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAoHtD,wBAAsB,eAAe,CAAC,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlF"}
1
+ {"version":3,"file":"migrate.pg.d.ts","sourceRoot":"","sources":["../src/migrate.pg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAsMtD,wBAAsB,eAAe,CAAC,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlF"}
@@ -54,6 +54,32 @@ const DDL = [
54
54
  max_steps INTEGER,
55
55
  scope TEXT NOT NULL, created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL)`,
56
56
  `CREATE UNIQUE INDEX IF NOT EXISTS latch_agents_owner_name ON latch_agents (owner, name)`,
57
+ // Append-only publish history for `latch_agents`. The live row is always a
58
+ // copy of the version its `published_version` names; rollback appends a NEW
59
+ // version carrying an old record, so history is never rewritten.
60
+ `CREATE TABLE IF NOT EXISTS latch_agent_versions (
61
+ id TEXT PRIMARY KEY, owner TEXT NOT NULL, name TEXT NOT NULL, version INTEGER NOT NULL,
62
+ record JSONB NOT NULL, author TEXT, source TEXT,
63
+ changed_fields JSONB, change_message TEXT, created_at BIGINT NOT NULL)`,
64
+ // Backstops the max(version)+1 race: a losing concurrent publish retries.
65
+ `CREATE UNIQUE INDEX IF NOT EXISTS latch_agent_versions_owner_name_version
66
+ ON latch_agent_versions (owner, name, version)`,
67
+ `CREATE INDEX IF NOT EXISTS latch_agent_versions_owner_name_idx
68
+ ON latch_agent_versions (owner, name)`,
69
+ // Who made which version live, and when. Activating an existing version
70
+ // appends no version, so without this a rollback leaves no trace.
71
+ `CREATE TABLE IF NOT EXISTS latch_agent_activations (
72
+ id TEXT PRIMARY KEY, owner TEXT NOT NULL, name TEXT NOT NULL, version INTEGER NOT NULL,
73
+ actor TEXT, source TEXT, created_at BIGINT NOT NULL)`,
74
+ `CREATE INDEX IF NOT EXISTS latch_agent_activations_owner_name_idx
75
+ ON latch_agent_activations (owner, name, created_at)`,
76
+ // One unpublished working copy per (agent, editor) — per PERSON, so two org
77
+ // admins can't overwrite each other's half-written prompt.
78
+ `CREATE TABLE IF NOT EXISTS latch_agent_drafts (
79
+ owner TEXT NOT NULL, name TEXT NOT NULL, editor TEXT NOT NULL,
80
+ record JSONB NOT NULL, base_version INTEGER, updated_at BIGINT NOT NULL,
81
+ PRIMARY KEY (owner, name, editor))`,
82
+ `CREATE INDEX IF NOT EXISTS latch_agent_drafts_owner_idx ON latch_agent_drafts (owner)`,
57
83
  `CREATE TABLE IF NOT EXISTS latch_schedules (
58
84
  id TEXT PRIMARY KEY, owner TEXT NOT NULL, agent TEXT NOT NULL, identity JSONB,
59
85
  cron TEXT, timezone TEXT, prompt TEXT NOT NULL, delivery JSONB,
@@ -108,13 +134,70 @@ const ALTERS = [
108
134
  `ALTER TABLE latch_chats ADD COLUMN IF NOT EXISTS kind TEXT NOT NULL DEFAULT 'user'`,
109
135
  `ALTER TABLE latch_memory_chat_state ADD COLUMN IF NOT EXISTS last_extracted_key TEXT`,
110
136
  `ALTER TABLE latch_agents ADD COLUMN IF NOT EXISTS attached_evals JSONB`,
137
+ `ALTER TABLE latch_agents ADD COLUMN IF NOT EXISTS published_version INTEGER`,
138
+ `ALTER TABLE latch_runs ADD COLUMN IF NOT EXISTS agent_version INTEGER`,
139
+ `ALTER TABLE latch_agent_versions ADD COLUMN IF NOT EXISTS changed_fields JSONB`,
140
+ `ALTER TABLE latch_agent_versions ADD COLUMN IF NOT EXISTS change_message TEXT`,
111
141
  // AFTER the kind ALTER above — a pre-kind DB can't index the column earlier.
112
142
  `CREATE INDEX IF NOT EXISTS latch_chats_sweep_idx ON latch_chats (kind, updated_at)`,
113
143
  ];
144
+ /**
145
+ * Data backfills. Run AFTER `ALTERS` (they reference columns the ALTERs add)
146
+ * and written to be idempotent, so every migrate can re-run them safely.
147
+ */
148
+ const BACKFILL = [
149
+ // Every pre-existing agent becomes v1, so the prompt that was already live is
150
+ // a rollback target. Without this the FIRST publish would create v1 from the
151
+ // NEW content and the running prompt would be lost for good — the exact
152
+ // failure version history exists to prevent. `WHERE NOT EXISTS` makes it a
153
+ // no-op once an agent has any version.
154
+ //
155
+ // `max_steps` is stored column-encoded (NULL=default, 0=unlimited, N=cap) and
156
+ // is decoded here to its `AgentRecord` form. `to_jsonb` on the integer keeps
157
+ // the numeric case a JSON number rather than a string.
158
+ `INSERT INTO latch_agent_versions (id, owner, name, version, record, author, source, created_at)
159
+ SELECT 'agv_' || replace(gen_random_uuid()::text, '-', ''), a.owner, a.name, 1,
160
+ jsonb_build_object(
161
+ 'name', a.name,
162
+ 'title', a.title,
163
+ 'description', a.description,
164
+ 'modelId', a.model_id,
165
+ 'instructions', a.instructions,
166
+ 'connections', a.connections,
167
+ 'tools', a.tools,
168
+ 'exec', a.exec,
169
+ 'subagents', a.subagents,
170
+ 'attachedEvals', a.attached_evals,
171
+ 'maxSteps', CASE WHEN a.max_steps IS NULL THEN NULL
172
+ WHEN a.max_steps = 0 THEN to_jsonb('unlimited'::text)
173
+ ELSE to_jsonb(a.max_steps) END,
174
+ 'scope', a.scope),
175
+ NULL, 'backfill', a.created_at
176
+ FROM latch_agents a
177
+ WHERE NOT EXISTS (SELECT 1 FROM latch_agent_versions v
178
+ WHERE v.owner = a.owner AND v.name = a.name)`,
179
+ // Point every live row at its version. Only fills NULLs, so a row that has
180
+ // published since the backfill keeps its real version.
181
+ `UPDATE latch_agents SET published_version = (
182
+ SELECT MAX(v.version) FROM latch_agent_versions v
183
+ WHERE v.owner = latch_agents.owner AND v.name = latch_agents.name)
184
+ WHERE published_version IS NULL`,
185
+ // ...and record that activation, so the publish timeline is complete from the
186
+ // first row rather than starting at whenever this feature shipped.
187
+ `INSERT INTO latch_agent_activations (id, owner, name, version, actor, source, created_at)
188
+ SELECT 'aga_' || replace(gen_random_uuid()::text, '-', ''), a.owner, a.name,
189
+ a.published_version, NULL, 'backfill', a.created_at
190
+ FROM latch_agents a
191
+ WHERE a.published_version IS NOT NULL
192
+ AND NOT EXISTS (SELECT 1 FROM latch_agent_activations x
193
+ WHERE x.owner = a.owner AND x.name = a.name)`,
194
+ ];
114
195
  export async function migratePostgres(db) {
115
196
  for (const stmt of DDL)
116
197
  await db.execute(sql.raw(stmt));
117
198
  for (const stmt of ALTERS)
118
199
  await db.execute(sql.raw(stmt));
200
+ for (const stmt of BACKFILL)
201
+ await db.execute(sql.raw(stmt));
119
202
  }
120
203
  //# sourceMappingURL=migrate.pg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.pg.js","sourceRoot":"","sources":["../src/migrate.pg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAGlC,MAAM,GAAG,GAAG;IACV;;;6DAG2D;IAC3D,yEAAyE;IACzE,0EAA0E;IAC1E,6EAA6E;IAC7E,uEAAuE;IACvE,qEAAqE;IACrE,kEAAkE;IAClE;;;gCAG8B;IAC9B,gGAAgG;IAChG;;;;;;;kDAOgD;IAChD,wEAAwE;IACxE,2FAA2F;IAC3F,6EAA6E;IAC7E,4EAA4E;IAC5E,+EAA+E;IAC/E;;;;;;iFAM+E;IAC/E,kHAAkH;IAClH;;;;iEAI+D;IAC/D,yEAAyE;IACzE;;;6DAG2D;IAC3D,6EAA6E;IAC7E;;;;;kFAKgF;IAChF,yFAAyF;IACzF;;;;;;iCAM+B;IAC/B,iFAAiF;IACjF,8FAA8F;IAC9F,0EAA0E;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,kEAAkE;IAClE;;;;;6DAK2D;IAC3D,qGAAqG;IACrG;;;;;;6DAM2D;IAC3D,+GAA+G;IAC/G;;;yCAGuC;IACvC;;;;iDAI+C;IAC/C,wGAAwG;CACzG,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,GAAG;IACb,oEAAoE;IACpE,qEAAqE;IACrE,qEAAqE;IACrE,wEAAwE;IACxE,iFAAiF;IACjF,kFAAkF;IAClF,2DAA2D;IAC3D,iEAAiE;IACjE,4EAA4E;IAC5E,6EAA6E;IAC7E,oFAAoF;IACpF,sFAAsF;IACtF,wEAAwE;IACxE,6EAA6E;IAC7E,oFAAoF;CACrF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAA6B;IACjE,KAAK,MAAM,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"migrate.pg.js","sourceRoot":"","sources":["../src/migrate.pg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAGlC,MAAM,GAAG,GAAG;IACV;;;6DAG2D;IAC3D,yEAAyE;IACzE,0EAA0E;IAC1E,6EAA6E;IAC7E,uEAAuE;IACvE,qEAAqE;IACrE,kEAAkE;IAClE;;;gCAG8B;IAC9B,gGAAgG;IAChG;;;;;;;kDAOgD;IAChD,wEAAwE;IACxE,2FAA2F;IAC3F,6EAA6E;IAC7E,4EAA4E;IAC5E,+EAA+E;IAC/E;;;;;;iFAM+E;IAC/E,kHAAkH;IAClH;;;;iEAI+D;IAC/D,yEAAyE;IACzE;;;6DAG2D;IAC3D,6EAA6E;IAC7E;;;;;kFAKgF;IAChF,yFAAyF;IACzF,2EAA2E;IAC3E,4EAA4E;IAC5E,iEAAiE;IACjE;;;4EAG0E;IAC1E,0EAA0E;IAC1E;oDACkD;IAClD;2CACyC;IACzC,wEAAwE;IACxE,kEAAkE;IAClE;;0DAEwD;IACxD;0DACwD;IACxD,4EAA4E;IAC5E,2DAA2D;IAC3D;;;wCAGsC;IACtC,uFAAuF;IACvF;;;;;;iCAM+B;IAC/B,iFAAiF;IACjF,8FAA8F;IAC9F,0EAA0E;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,kEAAkE;IAClE;;;;;6DAK2D;IAC3D,qGAAqG;IACrG;;;;;;6DAM2D;IAC3D,+GAA+G;IAC/G;;;yCAGuC;IACvC;;;;iDAI+C;IAC/C,wGAAwG;CACzG,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,GAAG;IACb,oEAAoE;IACpE,qEAAqE;IACrE,qEAAqE;IACrE,wEAAwE;IACxE,iFAAiF;IACjF,kFAAkF;IAClF,2DAA2D;IAC3D,iEAAiE;IACjE,4EAA4E;IAC5E,6EAA6E;IAC7E,oFAAoF;IACpF,sFAAsF;IACtF,wEAAwE;IACxE,6EAA6E;IAC7E,uEAAuE;IACvE,gFAAgF;IAChF,+EAA+E;IAC/E,6EAA6E;IAC7E,oFAAoF;CACrF,CAAC;AAEF;;;GAGG;AACH,MAAM,QAAQ,GAAG;IACf,8EAA8E;IAC9E,6EAA6E;IAC7E,wEAAwE;IACxE,2EAA2E;IAC3E,uCAAuC;IACvC,EAAE;IACF,8EAA8E;IAC9E,6EAA6E;IAC7E,uDAAuD;IACvD;;;;;;;;;;;;;;;;;;;;sEAoBoE;IACpE,2EAA2E;IAC3E,uDAAuD;IACvD;;;mCAGiC;IACjC,8EAA8E;IAC9E,mEAAmE;IACnE;;;;;;sEAMoE;CACrE,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAA6B;IACjE,KAAK,MAAM,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,KAAK,MAAM,IAAI,IAAI,QAAQ;QAAE,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC"}