neoctl 0.2.15 → 0.2.16

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.
Files changed (2) hide show
  1. package/dist/web/index.js +45 -1
  2. package/package.json +1 -1
package/dist/web/index.js CHANGED
@@ -360,6 +360,7 @@ export class WebRepl {
360
360
  queuedAttachments;
361
361
  foregroundRun;
362
362
  foregroundRunToken = 0;
363
+ pendingUserImageEchoLabels;
363
364
  backgroundSessionRuns = new Map();
364
365
  suppressReattachedStreaming = new Set();
365
366
  backgroundTaskCount;
@@ -705,6 +706,11 @@ export class WebRepl {
705
706
  }
706
707
  handleEvent(event) {
707
708
  this.reduce(event);
709
+ if (event.type === "message" && this.matchesPendingUserImageEcho(event.message)) {
710
+ this.pendingUserImageEchoLabels = undefined;
711
+ this.broadcastSync();
712
+ return;
713
+ }
708
714
  if (event.type === "state" || event.type === "context.metrics" || event.type === "usage" || event.type === "tool_call.delta" || event.type === "retrying") {
709
715
  this.broadcastSync();
710
716
  return;
@@ -789,6 +795,15 @@ export class WebRepl {
789
795
  if (event.type === "error")
790
796
  this.append({ kind: "error", text: event.error.message });
791
797
  }
798
+ matchesPendingUserImageEcho(message) {
799
+ const pending = this.pendingUserImageEchoLabels;
800
+ if (!pending?.length || message.role !== "user")
801
+ return false;
802
+ const labels = message.blocks
803
+ .filter((block) => block.type === "image" && typeof block.label === "string")
804
+ .map((block) => block.label);
805
+ return labels.length === pending.length && labels.every((label, index) => label === pending[index]);
806
+ }
792
807
  async handleCommandOrPrompt(text, attachments = []) {
793
808
  const command = parseReplCommand(text);
794
809
  if (command.type === "exit") {
@@ -888,6 +903,10 @@ export class WebRepl {
888
903
  this.append({ kind: "user", text: promptPayload.displayText });
889
904
  for (const line of imageLinesForBlocks("user", promptPayload.blocks))
890
905
  this.append(line);
906
+ const optimisticImageLabels = (promptPayload.blocks ?? [])
907
+ .filter((block) => block.type === "image" && typeof block.label === "string")
908
+ .map((block) => block.label);
909
+ this.pendingUserImageEchoLabels = optimisticImageLabels.length ? optimisticImageLabels : undefined;
891
910
  const runToken = ++this.foregroundRunToken;
892
911
  const abortController = new AbortController();
893
912
  this.activeAbortController = abortController;
@@ -927,6 +946,7 @@ export class WebRepl {
927
946
  this.activeAbortController = undefined;
928
947
  this.interruptArmed = false;
929
948
  this.finalizeForegroundView();
949
+ this.pendingUserImageEchoLabels = undefined;
930
950
  const queuedText = this.queuedInput;
931
951
  const queuedAttach = this.queuedAttachments;
932
952
  this.queuedInput = undefined;
@@ -1857,6 +1877,17 @@ function formatImageGenerationToolResult(output, ok) {
1857
1877
  const quality = typeof output.quality === "string" ? output.quality : undefined;
1858
1878
  const format = typeof output.outputFormat === "string" ? output.outputFormat : undefined;
1859
1879
  const sourceImages = typeof output.sourceImages === "number" ? output.sourceImages : undefined;
1880
+ const prompt = typeof output.prompt === "string" ? output.prompt.trim() : "";
1881
+ const semanticName = typeof output.semanticName === "string" ? output.semanticName.trim() : "";
1882
+ const background = typeof output.background === "string" ? output.background : undefined;
1883
+ const startedAtIso = typeof output.startedAtIso === "string" ? output.startedAtIso : undefined;
1884
+ const finishedAtIso = typeof output.finishedAtIso === "string" ? output.finishedAtIso : undefined;
1885
+ const revisedPrompt = Array.isArray(output.images)
1886
+ ? output.images
1887
+ .filter(isRecord)
1888
+ .map((image) => typeof image.revisedPrompt === "string" ? image.revisedPrompt.trim() : "")
1889
+ .find((value) => Boolean(value))
1890
+ : undefined;
1860
1891
  const lines = [`${mode === "edit" ? "edited" : "generated"} ${returnedImages ?? 0} image${returnedImages === 1 ? "" : "s"}`];
1861
1892
  const details = [provider, model, size, quality && quality !== "auto" ? quality : undefined, format].filter((value) => Boolean(value));
1862
1893
  if (details.length > 0)
@@ -1866,6 +1897,19 @@ function formatImageGenerationToolResult(output, ok) {
1866
1897
  const duration = imageGenerationDuration(output);
1867
1898
  if (duration !== undefined)
1868
1899
  lines.push(`duration: ${duration}ms`);
1900
+ if (prompt)
1901
+ lines.push(`prompt: ${prompt}`);
1902
+ if (revisedPrompt && revisedPrompt !== prompt)
1903
+ lines.push(`revisedPrompt: ${revisedPrompt}`);
1904
+ lines.push(`mode: ${mode}`);
1905
+ if (semanticName)
1906
+ lines.push(`semanticName: ${semanticName}`);
1907
+ if (background)
1908
+ lines.push(`background: ${background}`);
1909
+ if (startedAtIso)
1910
+ lines.push(`startedAt: ${startedAtIso}`);
1911
+ if (finishedAtIso)
1912
+ lines.push(`finishedAt: ${finishedAtIso}`);
1869
1913
  return lines.join("\n");
1870
1914
  }
1871
1915
  function imageGenerationDuration(output) {
@@ -2078,4 +2122,4 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
2078
2122
  process.exitCode = 1;
2079
2123
  });
2080
2124
  }
2081
- //# sourceMappingURL=index.js.map
2125
+ //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoctl",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "TypeScript scaffold for a streaming agent runtime with a REPL UI layer.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",