@ellipsis-dev/sdk 0.14.0 → 0.15.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.
@@ -116,32 +116,6 @@ function lifecycleText(recordType, payload) {
116
116
  }
117
117
 
118
118
  // src/store/transcript.ts
119
- var LineBuffer = class {
120
- buf = "";
121
- push(chunk) {
122
- this.buf += chunk;
123
- const parts = this.buf.split("\n");
124
- this.buf = parts.pop() ?? "";
125
- return parts;
126
- }
127
- flush() {
128
- const rest = this.buf.trim();
129
- this.buf = "";
130
- return rest ? [rest] : [];
131
- }
132
- };
133
- function parseEventLine(line) {
134
- const trimmed = line.trim();
135
- if (!trimmed) return null;
136
- try {
137
- const value = JSON.parse(trimmed);
138
- if (value && typeof value === "object" && !Array.isArray(value)) {
139
- return value;
140
- }
141
- } catch {
142
- }
143
- return null;
144
- }
145
119
  function oneLine(text, max) {
146
120
  const collapsed = text.replace(/\s+/g, " ").trim();
147
121
  return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 3)}...`;
@@ -176,35 +150,31 @@ function formatDuration(seconds) {
176
150
  }
177
151
  function blocksOf(content) {
178
152
  if (typeof content === "string") return [{ type: "text", text: content }];
179
- if (Array.isArray(content)) return content;
180
- return [];
153
+ return content;
181
154
  }
182
- function toolResultText(block) {
183
- const c = block.content;
184
- if (typeof c === "string") return c.trim();
185
- if (Array.isArray(c)) {
155
+ function toolResultText(content) {
156
+ if (typeof content === "string") return content.trim();
157
+ if (Array.isArray(content)) {
186
158
  const parts = [];
187
- for (const inner of c) {
159
+ for (const inner of content) {
188
160
  if (typeof inner.text === "string") parts.push(inner.text);
189
161
  else parts.push(JSON.stringify(inner));
190
162
  }
191
163
  return parts.join("\n").trim();
192
164
  }
193
- if (c === void 0 || c === null) return "";
194
- return JSON.stringify(c);
165
+ if (content === void 0 || content === null) return "";
166
+ return JSON.stringify(content);
195
167
  }
196
168
  function eventToItems(event, keyBase, options = {}) {
197
169
  const items = [];
198
170
  const push = (item) => {
199
171
  items.push({ ...item, key: `${keyBase}:${items.length}` });
200
172
  };
201
- const type = event.type;
202
- if (type === "result") {
173
+ if (event.kind === "result") {
203
174
  const bits = [];
204
- if (typeof event.duration_ms === "number")
205
- bits.push(formatDuration(event.duration_ms / 1e3));
206
- if (typeof event.total_cost_usd === "number")
207
- bits.push(`$${event.total_cost_usd.toFixed(2)}`);
175
+ bits.push(formatDuration(event.duration_ms / 1e3));
176
+ if (typeof event.cost_usd === "number")
177
+ bits.push(`$${event.cost_usd.toFixed(2)}`);
208
178
  const label = event.is_error ? "turn ended with an error" : "turn complete";
209
179
  push({
210
180
  kind: "summary",
@@ -214,11 +184,12 @@ function eventToItems(event, keyBase, options = {}) {
214
184
  });
215
185
  return items;
216
186
  }
217
- if (type === "system") {
187
+ if (event.kind === "system") {
218
188
  if (!options.systemInitLine || event.subtype !== "init") return items;
189
+ const data = event.data ?? {};
219
190
  const bits = [];
220
- if (typeof event.model === "string") bits.push(event.model);
221
- if (typeof event.cwd === "string") bits.push(event.cwd);
191
+ if (typeof data.model === "string") bits.push(data.model);
192
+ if (typeof data.cwd === "string") bits.push(data.cwd);
222
193
  push({
223
194
  kind: "system",
224
195
  text: bits.length ? `session started \xB7 ${bits.join(" \xB7 ")}` : "session started",
@@ -226,19 +197,20 @@ function eventToItems(event, keyBase, options = {}) {
226
197
  });
227
198
  return items;
228
199
  }
229
- const blocks = blocksOf(event.message?.content);
230
- if (type === "user") {
200
+ if (event.kind === "rate_limit") return items;
201
+ const blocks = blocksOf(event.content);
202
+ if (event.kind === "user") {
231
203
  for (const block of blocks) {
232
204
  if (block.type === "tool_result") {
233
- const body = toolResultText(block);
205
+ const body = toolResultText(block.content);
234
206
  push({
235
207
  kind: "tool_result",
236
208
  gutter: "\u23BF",
237
209
  text: body || "(no output)",
238
210
  spaceBefore: false,
239
- isError: block.is_error
211
+ isError: block.is_error ?? void 0
240
212
  });
241
- } else if (typeof block.text === "string" && block.text.trim()) {
213
+ } else if (block.type === "text" && block.text.trim()) {
242
214
  push({
243
215
  kind: "user",
244
216
  gutter: "\u203A",
@@ -250,37 +222,151 @@ function eventToItems(event, keyBase, options = {}) {
250
222
  return items;
251
223
  }
252
224
  for (const block of blocks) {
253
- if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim()) {
225
+ if (block.type === "thinking" && block.thinking.trim()) {
254
226
  push({
255
227
  kind: "thinking",
256
228
  gutter: "\u273B",
257
229
  text: block.thinking.trim(),
258
230
  spaceBefore: true
259
231
  });
260
- } else if (block.type === "tool_use") {
261
- const name = block.name ?? "tool";
262
- const summary = summarizeToolInput(name, block.input);
232
+ } else if (block.type === "tool_use" || block.type === "server_tool_use") {
233
+ const summary = summarizeToolInput(block.name, block.input);
263
234
  push({
264
235
  kind: "tool",
265
236
  gutter: "\u25CF",
266
- text: name,
237
+ text: block.name,
267
238
  detail: summary ? `(${summary})` : void 0,
268
239
  spaceBefore: true,
269
- tool: { name, input: block.input }
240
+ tool: { name: block.name, input: block.input }
270
241
  });
271
- } else if (typeof block.text === "string" && block.text.trim()) {
242
+ } else if (block.type === "text" && block.text.trim()) {
272
243
  push({ kind: "assistant", text: block.text.trim(), spaceBefore: true });
273
244
  }
274
245
  }
275
246
  return items;
276
247
  }
248
+ function codexEventToItems(event, keyBase) {
249
+ const items = [];
250
+ const push = (item2) => {
251
+ items.push({ ...item2, key: `${keyBase}:${items.length}` });
252
+ };
253
+ if (event.type === "turn.failed") {
254
+ push({
255
+ kind: "summary",
256
+ text: "turn ended with an error",
257
+ spaceBefore: true,
258
+ isError: true
259
+ });
260
+ return items;
261
+ }
262
+ if (event.type === "error") {
263
+ push({
264
+ kind: "error",
265
+ text: event.message?.trim() || "error",
266
+ spaceBefore: true,
267
+ isError: true
268
+ });
269
+ return items;
270
+ }
271
+ if (event.type !== "item.completed") return items;
272
+ const item = event.item;
273
+ switch (item.type) {
274
+ case "agent_message": {
275
+ const text = item.text.trim();
276
+ if (text) push({ kind: "assistant", text, spaceBefore: true });
277
+ break;
278
+ }
279
+ case "reasoning": {
280
+ const r = item;
281
+ const text = (r.text ?? r.summary ?? "").trim();
282
+ if (text)
283
+ push({ kind: "thinking", gutter: "\u273B", text, spaceBefore: true });
284
+ break;
285
+ }
286
+ case "command_execution": {
287
+ const c = item;
288
+ const summary = oneLine(c.command, 100);
289
+ push({
290
+ kind: "tool",
291
+ gutter: "\u25CF",
292
+ text: "Bash",
293
+ detail: summary ? `(${summary})` : void 0,
294
+ spaceBefore: true,
295
+ tool: { name: "Bash", input: { command: c.command } }
296
+ });
297
+ const output = (c.aggregated_output ?? "").trim();
298
+ push({
299
+ kind: "tool_result",
300
+ gutter: "\u23BF",
301
+ text: output || "(no output)",
302
+ spaceBefore: false,
303
+ isError: typeof c.exit_code === "number" && c.exit_code !== 0
304
+ });
305
+ break;
306
+ }
307
+ case "file_change": {
308
+ const paths = codexChangedPaths(item);
309
+ push({
310
+ kind: "tool",
311
+ gutter: "\u25CF",
312
+ text: "Edit",
313
+ detail: paths.length ? `(${oneLine(paths.join(", "), 100)})` : void 0,
314
+ spaceBefore: true,
315
+ tool: { name: "Edit" }
316
+ });
317
+ break;
318
+ }
319
+ case "mcp_tool_call": {
320
+ const name = codexMcpToolName(item);
321
+ push({
322
+ kind: "tool",
323
+ gutter: "\u25CF",
324
+ text: name,
325
+ spaceBefore: true,
326
+ tool: { name }
327
+ });
328
+ break;
329
+ }
330
+ case "web_search": {
331
+ const query = item.query;
332
+ push({
333
+ kind: "tool",
334
+ gutter: "\u25CF",
335
+ text: "WebSearch",
336
+ detail: query ? `(${oneLine(query, 100)})` : void 0,
337
+ spaceBefore: true,
338
+ tool: { name: "WebSearch" }
339
+ });
340
+ break;
341
+ }
342
+ case "error":
343
+ push({
344
+ kind: "error",
345
+ text: item.message?.trim() || "error",
346
+ spaceBefore: true,
347
+ isError: true
348
+ });
349
+ break;
350
+ }
351
+ return items;
352
+ }
353
+ function codexChangedPaths(item) {
354
+ return (item.changes ?? []).map((c) => typeof c.path === "string" ? c.path : null).filter((p) => p !== null);
355
+ }
356
+ function codexMcpToolName(item) {
357
+ return item.server && item.tool ? `mcp__${item.server}__${item.tool}` : item.tool ?? "mcp_tool_call";
358
+ }
277
359
  function recordToItems(record, keyBase, options = {}) {
278
- if (record.source === "lifecycle") {
279
- const text = lifecycleText(record.record_type, record.payload);
280
- return text ? [{ key: keyBase, kind: "notice", text, spaceBefore: true }] : [];
360
+ switch (record.record_format) {
361
+ case "claude_sdk@1":
362
+ return eventToItems(record.payload, keyBase, options);
363
+ case "codex_jsonl@1":
364
+ return codexEventToItems(record.payload, keyBase);
365
+ case "ellipsis_lifecycle@1": {
366
+ const text = lifecycleText(record.record_type, record.payload);
367
+ return text ? [{ key: keyBase, kind: "notice", text, spaceBefore: true }] : [];
368
+ }
281
369
  }
282
- if (record.source !== "claude_code") return [];
283
- return eventToItems(record.payload, keyBase, options);
284
370
  }
285
371
  function isConnectVisibleRecord(record) {
286
372
  return record.source !== "lifecycle" || record.record_type === "sandbox_ready";
@@ -341,19 +427,18 @@ function clampLines(text, maxLines) {
341
427
  };
342
428
  }
343
429
  function resultCostUsd(event) {
344
- if (event.type !== "result") return null;
345
- return typeof event.total_cost_usd === "number" ? event.total_cost_usd : null;
430
+ if (event.kind !== "result") return null;
431
+ return typeof event.cost_usd === "number" ? event.cost_usd : null;
346
432
  }
347
433
  function foldCosts(events) {
348
- let prev = null;
349
434
  let total = null;
435
+ let lastStep = null;
350
436
  for (const event of events) {
351
437
  const cost = resultCostUsd(event);
352
438
  if (cost == null) continue;
353
- prev = total;
354
- total = cost;
439
+ total = (total ?? 0) + cost;
440
+ lastStep = cost;
355
441
  }
356
- const lastStep = total != null && prev != null ? Math.max(0, total - prev) : total;
357
442
  return { total, lastStep };
358
443
  }
359
444
  function statusActivityText(status) {
@@ -370,36 +455,19 @@ function statusActivityText(status) {
370
455
  }
371
456
 
372
457
  // src/store/chatTurns.ts
373
- var isInitEvent = (data) => data.type === "system" && data.subtype === "init";
374
- var TOOL_USE_BLOCK_TYPES = /* @__PURE__ */ new Set([
375
- "tool_use",
376
- "server_tool_use",
377
- "mcp_tool_use"
378
- ]);
458
+ var isInitEvent = (data) => data.kind === "system" && data.subtype === "init";
379
459
  var SANDBOX_RECORD_TYPES = /* @__PURE__ */ new Set([
380
460
  "sandbox_starting",
381
461
  "sandbox_phase",
382
462
  "sandbox_output",
383
463
  "sandbox_ready"
384
464
  ]);
385
- function toolResultText2(block) {
386
- const c = block.content;
387
- if (typeof c === "string") return c.trim();
388
- if (Array.isArray(c)) {
389
- return c.map(
390
- (inner) => typeof inner.text === "string" ? inner.text : JSON.stringify(inner)
391
- ).join("\n").trim();
392
- }
393
- if (c === void 0 || c === null) return "";
394
- return JSON.stringify(c);
395
- }
396
465
  function groupRecordsToChatTurns(records) {
397
466
  const turns = [];
398
467
  const toolNodeByUseId = /* @__PURE__ */ new Map();
399
468
  let seq = 0;
400
469
  const nextKey = (base) => `${base}:${seq++}`;
401
470
  let openIdx = -1;
402
- let prevCumulativeCostUsd = 0;
403
471
  let initCount = 0;
404
472
  let pendingResume = false;
405
473
  const openAgentTurn = (record) => {
@@ -421,7 +489,7 @@ function groupRecordsToChatTurns(records) {
421
489
  return turns[openIdx];
422
490
  };
423
491
  for (const record of records) {
424
- if (record.source === "lifecycle") {
492
+ if (record.record_format === "ellipsis_lifecycle@1") {
425
493
  const text = lifecycleText(record.record_type, record.payload);
426
494
  if (!text) continue;
427
495
  const isSandbox = SANDBOX_RECORD_TYPES.has(record.record_type);
@@ -483,43 +551,49 @@ function groupRecordsToChatTurns(records) {
483
551
  });
484
552
  continue;
485
553
  }
486
- if (record.source !== "claude_code") continue;
554
+ if (record.record_format === "codex_jsonl@1") {
555
+ codexEventIntoTurns(record, record.payload, {
556
+ openAgentTurn,
557
+ closeTurn: (completedAt) => {
558
+ if (openIdx >= 0) {
559
+ turns[openIdx].completedAt = completedAt;
560
+ openIdx = -1;
561
+ }
562
+ },
563
+ nextKey
564
+ });
565
+ continue;
566
+ }
487
567
  const data = record.payload;
488
- if (data.type === "system") {
568
+ if (data.kind === "system") {
489
569
  if (isInitEvent(data)) {
490
570
  initCount += 1;
491
571
  if (initCount > 1) pendingResume = true;
492
572
  }
493
573
  continue;
494
574
  }
495
- if (data.type === "result") {
575
+ if (data.kind === "result") {
496
576
  if (openIdx >= 0) {
497
577
  turns[openIdx].completedAt = record.created_at;
498
578
  turns[openIdx].durationMs = typeof data.duration_ms === "number" ? data.duration_ms : null;
499
- if (typeof data.total_cost_usd === "number") {
500
- turns[openIdx].costUsd = Math.max(
501
- 0,
502
- data.total_cost_usd - prevCumulativeCostUsd
503
- );
504
- prevCumulativeCostUsd = data.total_cost_usd;
579
+ if (typeof data.cost_usd === "number") {
580
+ turns[openIdx].costUsd = data.cost_usd;
505
581
  }
506
582
  openIdx = -1;
507
583
  }
508
584
  continue;
509
585
  }
510
- const message = data.message;
511
- if (!message) continue;
512
- if (data.type === "user") {
513
- const content2 = message.content;
514
- if (typeof content2 === "string") {
515
- if (content2.trim()) {
586
+ if (data.kind === "user") {
587
+ const content = data.content;
588
+ if (typeof content === "string") {
589
+ if (content.trim()) {
516
590
  openIdx = -1;
517
591
  turns.push({
518
592
  resumed: false,
519
593
  key: nextKey(record.id),
520
594
  role: "user",
521
595
  nodes: [
522
- { key: nextKey(record.id), kind: "user", text: content2.trim() }
596
+ { key: nextKey(record.id), kind: "user", text: content.trim() }
523
597
  ],
524
598
  startedAt: record.created_at,
525
599
  completedAt: record.created_at,
@@ -530,12 +604,11 @@ function groupRecordsToChatTurns(records) {
530
604
  }
531
605
  continue;
532
606
  }
533
- if (!Array.isArray(content2)) continue;
534
- for (const block of content2) {
607
+ for (const block of content) {
535
608
  if (block.type !== "tool_result") continue;
536
- const body = toolResultText2(block) || "(no output)";
537
- const useId = block.tool_use_id ?? void 0;
538
- const node = useId != null ? toolNodeByUseId.get(useId) : void 0;
609
+ const body = toolResultText(block.content) || "(no output)";
610
+ const useId = block.tool_use_id;
611
+ const node = toolNodeByUseId.get(useId);
539
612
  if (node) {
540
613
  node.result = body;
541
614
  node.isError = !!block.is_error;
@@ -553,41 +626,38 @@ function groupRecordsToChatTurns(records) {
553
626
  completedAt: record.created_at
554
627
  };
555
628
  openAgentTurn(record).nodes.push(orphan);
556
- if (useId != null) toolNodeByUseId.set(useId, orphan);
629
+ toolNodeByUseId.set(useId, orphan);
557
630
  }
558
631
  }
559
632
  continue;
560
633
  }
561
- const content = message.content;
562
- if (!Array.isArray(content)) continue;
634
+ if (data.kind !== "assistant") continue;
563
635
  const turn = openAgentTurn(record);
564
- const usage = message.usage;
565
- if (usage && typeof usage.output_tokens === "number") {
566
- turn.tokens = (turn.tokens ?? 0) + usage.output_tokens;
636
+ if (data.usage && typeof data.usage.output_tokens === "number") {
637
+ turn.tokens = (turn.tokens ?? 0) + data.usage.output_tokens;
567
638
  }
568
- for (const block of content) {
569
- if ((block.type === "thinking" || block.type === "redacted_thinking") && typeof block.thinking === "string" && block.thinking.trim()) {
639
+ for (const block of data.content) {
640
+ if (block.type === "thinking" && block.thinking.trim()) {
570
641
  turn.nodes.push({
571
642
  key: nextKey(record.id),
572
643
  kind: "thinking",
573
644
  text: block.thinking.trim()
574
645
  });
575
- } else if (block.type != null && TOOL_USE_BLOCK_TYPES.has(block.type)) {
576
- const name = block.name ?? "tool";
646
+ } else if (block.type === "tool_use" || block.type === "server_tool_use") {
577
647
  const node = {
578
648
  key: nextKey(record.id),
579
649
  kind: "tool",
580
- name,
581
- input: block.input ?? null,
582
- summary: summarizeToolInput(name, block.input ?? void 0),
650
+ name: block.name,
651
+ input: block.input,
652
+ summary: summarizeToolInput(block.name, block.input),
583
653
  result: null,
584
654
  isError: false,
585
655
  startedAt: record.created_at,
586
656
  completedAt: null
587
657
  };
588
658
  turn.nodes.push(node);
589
- if (block.id) toolNodeByUseId.set(block.id, node);
590
- } else if (block.type === "text" && typeof block.text === "string" && block.text.trim()) {
659
+ toolNodeByUseId.set(block.id, node);
660
+ } else if (block.type === "text" && block.text.trim()) {
591
661
  turn.nodes.push({
592
662
  key: nextKey(record.id),
593
663
  kind: "assistant",
@@ -598,6 +668,105 @@ function groupRecordsToChatTurns(records) {
598
668
  }
599
669
  return turns;
600
670
  }
671
+ function codexEventIntoTurns(record, event, ops) {
672
+ if (event.type === "turn.completed" || event.type === "turn.failed") {
673
+ ops.closeTurn(record.created_at);
674
+ return;
675
+ }
676
+ if (event.type === "error") {
677
+ const turn2 = ops.openAgentTurn(record);
678
+ turn2.nodes.push({
679
+ key: ops.nextKey(record.id),
680
+ kind: "assistant",
681
+ text: event.message?.trim() || "error"
682
+ });
683
+ return;
684
+ }
685
+ if (event.type !== "item.completed") return;
686
+ const item = event.item;
687
+ const turn = () => ops.openAgentTurn(record);
688
+ switch (item.type) {
689
+ case "agent_message": {
690
+ const text = item.text.trim();
691
+ if (text)
692
+ turn().nodes.push({
693
+ key: ops.nextKey(record.id),
694
+ kind: "assistant",
695
+ text
696
+ });
697
+ break;
698
+ }
699
+ case "reasoning": {
700
+ const r = item;
701
+ const text = (r.text ?? r.summary ?? "").trim();
702
+ if (text)
703
+ turn().nodes.push({
704
+ key: ops.nextKey(record.id),
705
+ kind: "thinking",
706
+ text
707
+ });
708
+ break;
709
+ }
710
+ case "command_execution": {
711
+ const c = item;
712
+ turn().nodes.push({
713
+ key: ops.nextKey(record.id),
714
+ kind: "tool",
715
+ name: "Bash",
716
+ input: { command: c.command },
717
+ summary: oneLine(c.command, 100),
718
+ result: (c.aggregated_output ?? "").trim() || "(no output)",
719
+ isError: typeof c.exit_code === "number" && c.exit_code !== 0,
720
+ startedAt: record.created_at,
721
+ completedAt: record.created_at
722
+ });
723
+ break;
724
+ }
725
+ case "file_change": {
726
+ const paths = codexChangedPaths(item);
727
+ turn().nodes.push({
728
+ key: ops.nextKey(record.id),
729
+ kind: "tool",
730
+ name: "Edit",
731
+ input: null,
732
+ summary: paths.length ? oneLine(paths.join(", "), 100) : "",
733
+ result: null,
734
+ isError: false,
735
+ startedAt: record.created_at,
736
+ completedAt: record.created_at
737
+ });
738
+ break;
739
+ }
740
+ case "mcp_tool_call":
741
+ turn().nodes.push({
742
+ key: ops.nextKey(record.id),
743
+ kind: "tool",
744
+ name: codexMcpToolName(item),
745
+ input: null,
746
+ summary: "",
747
+ result: null,
748
+ isError: false,
749
+ startedAt: record.created_at,
750
+ completedAt: record.created_at
751
+ });
752
+ break;
753
+ case "web_search": {
754
+ const query = item.query;
755
+ turn().nodes.push({
756
+ key: ops.nextKey(record.id),
757
+ kind: "tool",
758
+ name: "WebSearch",
759
+ input: query ? { query } : null,
760
+ summary: query ? oneLine(query, 100) : "",
761
+ result: null,
762
+ isError: false,
763
+ startedAt: record.created_at,
764
+ completedAt: record.created_at
765
+ });
766
+ break;
767
+ }
768
+ }
769
+ }
601
770
 
602
771
  // src/store/index.ts
603
772
  var TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set([
@@ -791,10 +960,12 @@ var SessionTranscriptStore = class {
791
960
  };
792
961
  };
793
962
  export {
794
- LineBuffer,
795
963
  SessionTranscriptStore,
796
964
  cacheTierLabel,
797
965
  clampLines,
966
+ codexChangedPaths,
967
+ codexEventToItems,
968
+ codexMcpToolName,
798
969
  collapseToolRuns,
799
970
  emptySessionTranscriptSnapshot,
800
971
  eventToItems,
@@ -805,7 +976,6 @@ export {
805
976
  isConversationOver,
806
977
  lifecycleText,
807
978
  oneLine,
808
- parseEventLine,
809
979
  pendingToolCalls,
810
980
  recordToItems,
811
981
  resultCostUsd,
@@ -814,5 +984,6 @@ export {
814
984
  sandboxOutputStep,
815
985
  sandboxPhaseLabel,
816
986
  statusActivityText,
817
- summarizeToolInput
987
+ summarizeToolInput,
988
+ toolResultText
818
989
  };
@@ -1,4 +1,4 @@
1
- import { X as StreamFrame, r as Session } from '../types-egqnpL5C.js';
1
+ import { a7 as StreamFrame, K as Session } from '../types-DtVn9Yj3.js';
2
2
 
3
3
  declare const SESSION_STREAM_PROTOCOL_VERSION = 3;
4
4
  declare const WS_CLOSE_NORMAL = 1000;