@alas-ide/codex-acp 2.6.0-alas.1 → 2.6.0-alas.2

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/index.js +136 -86
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -36335,6 +36335,109 @@ function hasOnlyKeys(value, allowed) {
36335
36335
  return Object.keys(value).every((key) => allowedKeys.has(key));
36336
36336
  }
36337
36337
 
36338
+ // src/PromptSuggestionGenerator.ts
36339
+ var PROMPT_SUGGESTIONS_META_KEY = "promptSuggestions";
36340
+ var SUGGESTION_EFFORT = "high";
36341
+ var SUGGESTION_SCHEMA = {
36342
+ type: "object",
36343
+ properties: { suggestion: { type: ["string", "null"] } },
36344
+ required: ["suggestion"],
36345
+ additionalProperties: false
36346
+ };
36347
+ var SUGGESTION_PROMPT = "This is an app-generated request for a suggested next user message. The user did not write this message.\n\nYour suggestion will appear in the user's empty message composer. They can accept it, edit it, or ignore it; it will not be sent automatically.\n\nPredict the single message this user is most likely to type next from the visible conversation. Use the whole conversation to understand their current intent and the latest exchange. Choose the action and specific object that best continue that intent, rather than a generally useful follow-up. Express its core naturally in the user's voice, keeping the requirements needed to retain its meaning. Choose the strongest grounded move even if several continuations are possible; uncertainty about exact wording or incidental details is not a reason to return null. Use null only when the conversation offers no grounded next move at all.\n\nSet suggestion to exactly one natural user message, one line, at most 240 characters, or null. No alternatives or explanation.\n\nReturn only JSON matching this schema: " + JSON.stringify(SUGGESTION_SCHEMA);
36348
+ function promptSuggestionsEnabled(env = process.env) {
36349
+ return env["CODEX_ACP_PROMPT_SUGGESTIONS"]?.trim().toLowerCase() !== "false";
36350
+ }
36351
+ function promptSuggestionsMeta(text, requestId) {
36352
+ return {
36353
+ [PROMPT_SUGGESTIONS_META_KEY]: {
36354
+ suggestions: text === null ? [] : [{ text }],
36355
+ ...requestId !== void 0 ? { id: requestId } : {}
36356
+ }
36357
+ };
36358
+ }
36359
+ function promptSuggestionsRequestId(meta3) {
36360
+ if (meta3 === null || typeof meta3 !== "object") return void 0;
36361
+ const value = meta3[PROMPT_SUGGESTIONS_META_KEY];
36362
+ if (value === null || typeof value !== "object") return void 0;
36363
+ const id = value["id"];
36364
+ return typeof id === "string" && id.length > 0 && id.length <= 256 ? id : void 0;
36365
+ }
36366
+ var PromptSuggestionGenerator = class {
36367
+ constructor(client, publish) {
36368
+ this.client = client;
36369
+ this.publish = publish;
36370
+ }
36371
+ client;
36372
+ publish;
36373
+ generation = 0;
36374
+ lastPublished = null;
36375
+ /**
36376
+ * Fire-and-forget hook, called after a turn ends normally. A newer turn or {@link invalidate} drops its result.
36377
+ * With a `requestId` the result always goes out with it, an empty list when the sample gives nothing or fails,
36378
+ * so a client-neutral client waiting for that id can stop.
36379
+ */
36380
+ onTurnCompleted(threadId, turnId, requestId) {
36381
+ const generation = ++this.generation;
36382
+ this.generateAndPublish(generation, threadId, turnId, requestId).catch(async () => {
36383
+ if (requestId === void 0 || generation !== this.generation) return;
36384
+ await this.publishAnswer(null, requestId).catch(() => {
36385
+ });
36386
+ });
36387
+ }
36388
+ /** A new prompt started or the session closed: a result still being generated is dropped. */
36389
+ invalidate() {
36390
+ this.generation++;
36391
+ }
36392
+ /**
36393
+ * Drops a result in flight and clears the suggestion the client holds. Returns `null` when nothing is held,
36394
+ * so the caller does not wait a tick for nothing.
36395
+ */
36396
+ clear() {
36397
+ this.invalidate();
36398
+ if (this.lastPublished === null) return null;
36399
+ return this.publishIfChanged(null).catch(() => {
36400
+ });
36401
+ }
36402
+ /** The app-server restarted: later generations go to `client`. A generation in flight ends with the old one. */
36403
+ rebindAppServer(client) {
36404
+ this.client = client;
36405
+ }
36406
+ async generateAndPublish(generation, threadId, turnId, requestId) {
36407
+ const client = this.client;
36408
+ const response = await client.threadSample({
36409
+ threadId,
36410
+ expectedTurnId: turnId,
36411
+ prompt: SUGGESTION_PROMPT,
36412
+ effort: SUGGESTION_EFFORT
36413
+ });
36414
+ if (generation !== this.generation) return;
36415
+ const text = parseSuggestion(response.text);
36416
+ if (requestId !== void 0) await this.publishAnswer(text, requestId);
36417
+ else await this.publishIfChanged(text);
36418
+ }
36419
+ /** Answers a client-neutral request: it goes out even when it repeats the text the client holds. */
36420
+ async publishAnswer(text, requestId) {
36421
+ this.lastPublished = text;
36422
+ await this.publish(text, requestId);
36423
+ }
36424
+ async publishIfChanged(text) {
36425
+ if (text === this.lastPublished) return;
36426
+ this.lastPublished = text;
36427
+ await this.publish(text);
36428
+ }
36429
+ };
36430
+ function parseSuggestion(text) {
36431
+ if (!text?.trim()) return null;
36432
+ try {
36433
+ const value = JSON.parse(text).suggestion;
36434
+ if (typeof value !== "string") return null;
36435
+ return value.trim() || null;
36436
+ } catch {
36437
+ return null;
36438
+ }
36439
+ }
36440
+
36338
36441
  // src/tool-calls/ClientCapabilities.ts
36339
36442
  var ClientCapabilities = class _ClientCapabilities {
36340
36443
  static DEFAULT = new _ClientCapabilities({
@@ -36353,7 +36456,8 @@ var ClientCapabilities = class _ClientCapabilities {
36353
36456
  neutralPermissionPresentation: false,
36354
36457
  neutralQuestionCustomAnswer: false,
36355
36458
  neutralRecommendedConfigValue: false,
36356
- neutralCommandPresentation: false
36459
+ neutralCommandPresentation: false,
36460
+ neutralPromptSuggestions: false
36357
36461
  });
36358
36462
  /** The client declares `_meta.jetbrains.air`. */
36359
36463
  airClient;
@@ -36383,6 +36487,11 @@ var ClientCapabilities = class _ClientCapabilities {
36383
36487
  neutralRecommendedConfigValue;
36384
36488
  /** Available commands carry `_meta.commandPresentation`. Only ACP v1 reads the opt-in. */
36385
36489
  neutralCommandPresentation;
36490
+ /**
36491
+ * A prompt that names `_meta.promptSuggestions.id` gets the predicted follow-up prompt of its turn.
36492
+ * Only ACP v1 reads the opt-in.
36493
+ */
36494
+ neutralPromptSuggestions;
36386
36495
  constructor(values) {
36387
36496
  this.airClient = values.airClient;
36388
36497
  this.airToolCallContract = values.airToolCallContract;
@@ -36400,6 +36509,11 @@ var ClientCapabilities = class _ClientCapabilities {
36400
36509
  this.neutralQuestionCustomAnswer = values.neutralQuestionCustomAnswer;
36401
36510
  this.neutralRecommendedConfigValue = values.neutralRecommendedConfigValue;
36402
36511
  this.neutralCommandPresentation = values.neutralCommandPresentation;
36512
+ this.neutralPromptSuggestions = values.neutralPromptSuggestions;
36513
+ }
36514
+ /** Whether the client reads predicted follow-up prompts, in any form. */
36515
+ get predictsPrompts() {
36516
+ return this.airClient || this.neutralPromptSuggestions;
36403
36517
  }
36404
36518
  static from(capabilities) {
36405
36519
  const airClient = isAirClient(capabilities);
@@ -36428,7 +36542,8 @@ var ClientCapabilities = class _ClientCapabilities {
36428
36542
  neutralPermissionPresentation: clientRequestsNeutralPresentation(capabilities, NEUTRAL_PERMISSION_PRESENTATION_META_KEY),
36429
36543
  neutralQuestionCustomAnswer: clientRequestsNeutralPresentation(capabilities, NEUTRAL_QUESTION_CUSTOM_ANSWER_META_KEY),
36430
36544
  neutralRecommendedConfigValue: clientRequestsNeutralPresentation(capabilities, NEUTRAL_RECOMMENDED_CONFIG_VALUE_META_KEY),
36431
- neutralCommandPresentation: clientRequestsNeutralPresentation(capabilities, NEUTRAL_COMMAND_PRESENTATION_META_KEY)
36545
+ neutralCommandPresentation: clientRequestsNeutralPresentation(capabilities, NEUTRAL_COMMAND_PRESENTATION_META_KEY),
36546
+ neutralPromptSuggestions: clientRequestsNeutralPresentation(capabilities, PROMPT_SUGGESTIONS_META_KEY)
36432
36547
  });
36433
36548
  }
36434
36549
  /**
@@ -36459,7 +36574,8 @@ var ClientCapabilities = class _ClientCapabilities {
36459
36574
  neutralPermissionPresentation: false,
36460
36575
  neutralQuestionCustomAnswer: false,
36461
36576
  neutralRecommendedConfigValue: false,
36462
- neutralCommandPresentation: false
36577
+ neutralCommandPresentation: false,
36578
+ neutralPromptSuggestions: false
36463
36579
  });
36464
36580
  }
36465
36581
  /**
@@ -42151,7 +42267,7 @@ var package_default = {
42151
42267
  publishConfig: {
42152
42268
  access: "public"
42153
42269
  },
42154
- version: "2.6.0-alas.1",
42270
+ version: "2.6.0-alas.2",
42155
42271
  description: "",
42156
42272
  main: "dist/index.js",
42157
42273
  bin: {
@@ -42229,7 +42345,7 @@ var package_default = {
42229
42345
  alasDownstream: {
42230
42346
  upstreamVersion: "2.6.0",
42231
42347
  upstreamCommit: "f3208913693bd4d5171d6c6b9be5ec1ff96c6431",
42232
- sourceCommit: "7010b594e77f4f941a0712bea03aae93a8bc20dc"
42348
+ sourceCommit: "e8e45a368d0d1f832fbd5307e1c08e879d1c9de3"
42233
42349
  }
42234
42350
  };
42235
42351
 
@@ -50759,83 +50875,6 @@ function titleSource(text) {
50759
50875
  return text.slice(0, end);
50760
50876
  }
50761
50877
 
50762
- // src/PromptSuggestionGenerator.ts
50763
- var PROMPT_SUGGESTIONS_META_KEY = "promptSuggestions";
50764
- var SUGGESTION_EFFORT = "high";
50765
- var SUGGESTION_SCHEMA = {
50766
- type: "object",
50767
- properties: { suggestion: { type: ["string", "null"] } },
50768
- required: ["suggestion"],
50769
- additionalProperties: false
50770
- };
50771
- var SUGGESTION_PROMPT = "This is an app-generated request for a suggested next user message. The user did not write this message.\n\nYour suggestion will appear in the user's empty message composer. They can accept it, edit it, or ignore it; it will not be sent automatically.\n\nPredict the single message this user is most likely to type next from the visible conversation. Use the whole conversation to understand their current intent and the latest exchange. Choose the action and specific object that best continue that intent, rather than a generally useful follow-up. Express its core naturally in the user's voice, keeping the requirements needed to retain its meaning. Choose the strongest grounded move even if several continuations are possible; uncertainty about exact wording or incidental details is not a reason to return null. Use null only when the conversation offers no grounded next move at all.\n\nSet suggestion to exactly one natural user message, one line, at most 240 characters, or null. No alternatives or explanation.\n\nReturn only JSON matching this schema: " + JSON.stringify(SUGGESTION_SCHEMA);
50772
- function promptSuggestionsEnabled(env = process.env) {
50773
- return env["CODEX_ACP_PROMPT_SUGGESTIONS"]?.trim().toLowerCase() !== "false";
50774
- }
50775
- function promptSuggestionsMeta(text) {
50776
- return { [PROMPT_SUGGESTIONS_META_KEY]: { suggestions: text === null ? [] : [{ text }] } };
50777
- }
50778
- var PromptSuggestionGenerator = class {
50779
- constructor(client, publish) {
50780
- this.client = client;
50781
- this.publish = publish;
50782
- }
50783
- client;
50784
- publish;
50785
- generation = 0;
50786
- lastPublished = null;
50787
- /** Fire-and-forget hook, called after a turn ends normally. A newer turn or {@link invalidate} drops its result. */
50788
- onTurnCompleted(threadId, turnId) {
50789
- const generation = ++this.generation;
50790
- this.generateAndPublish(generation, threadId, turnId).catch(() => {
50791
- });
50792
- }
50793
- /** A new prompt started or the session closed: a result still being generated is dropped. */
50794
- invalidate() {
50795
- this.generation++;
50796
- }
50797
- /**
50798
- * Drops a result in flight and clears the suggestion the client holds. Returns `null` when nothing is held,
50799
- * so the caller does not wait a tick for nothing.
50800
- */
50801
- clear() {
50802
- this.invalidate();
50803
- if (this.lastPublished === null) return null;
50804
- return this.publishIfChanged(null).catch(() => {
50805
- });
50806
- }
50807
- /** The app-server restarted: later generations go to `client`. A generation in flight ends with the old one. */
50808
- rebindAppServer(client) {
50809
- this.client = client;
50810
- }
50811
- async generateAndPublish(generation, threadId, turnId) {
50812
- const client = this.client;
50813
- const response = await client.threadSample({
50814
- threadId,
50815
- expectedTurnId: turnId,
50816
- prompt: SUGGESTION_PROMPT,
50817
- effort: SUGGESTION_EFFORT
50818
- });
50819
- if (generation !== this.generation) return;
50820
- await this.publishIfChanged(parseSuggestion(response.text));
50821
- }
50822
- async publishIfChanged(text) {
50823
- if (text === this.lastPublished) return;
50824
- this.lastPublished = text;
50825
- await this.publish(text);
50826
- }
50827
- };
50828
- function parseSuggestion(text) {
50829
- if (!text?.trim()) return null;
50830
- try {
50831
- const value = JSON.parse(text).suggestion;
50832
- if (typeof value !== "string") return null;
50833
- return value.trim() || null;
50834
- } catch {
50835
- return null;
50836
- }
50837
- }
50838
-
50839
50878
  // src/SessionIndex.ts
50840
50879
  var AIR_SESSION_INDEX_KEY = "sessionIndex";
50841
50880
  var AIR_SESSION_BATCH_GET_KEY = "sessionBatchGet";
@@ -53901,6 +53940,10 @@ var CodexAcpServer = class {
53901
53940
  ...this.capabilities.neutralCommandPresentation ? {
53902
53941
  [NEUTRAL_COMMAND_PRESENTATION_META_KEY]: neutralPresentationCapability()
53903
53942
  } : {},
53943
+ // Only while the adapter predicts at all: `CODEX_ACP_PROMPT_SUGGESTIONS=false` turns it off.
53944
+ ...this.capabilities.neutralPromptSuggestions && promptSuggestionsEnabled() ? {
53945
+ [PROMPT_SUGGESTIONS_META_KEY]: neutralPresentationCapability()
53946
+ } : {},
53904
53947
  // The session index for a client that opted in without AIR, see `docs/air-extensions.md`.
53905
53948
  ...neutralSessionIndex ? {
53906
53949
  [NEUTRAL_SESSION_INDEX_META_KEY]: neutralSessionIndex
@@ -56673,13 +56716,13 @@ You have been logged out. Please try again.`);
56673
56716
  return null;
56674
56717
  }
56675
56718
  createPromptSuggestions(sessionState) {
56676
- if (!sessionState.clientCapabilities.airClient || !promptSuggestionsEnabled()) return void 0;
56719
+ if (!sessionState.clientCapabilities.predictsPrompts || !promptSuggestionsEnabled()) return void 0;
56677
56720
  return new PromptSuggestionGenerator(
56678
56721
  this.codexAcpClient.appServerClient,
56679
- async (text) => {
56722
+ async (text, requestId) => {
56680
56723
  await new ACPSessionConnection(this.connection, sessionState.sessionId).update({
56681
56724
  sessionUpdate: "session_info_update",
56682
- _meta: promptSuggestionsMeta(text)
56725
+ _meta: promptSuggestionsMeta(text, requestId)
56683
56726
  });
56684
56727
  }
56685
56728
  );
@@ -57863,7 +57906,14 @@ You have been logged out. Please try again.`);
57863
57906
  );
57864
57907
  const promptText = params.prompt.filter((b) => b.type === "text").map((b) => b.text).join(" ").trim();
57865
57908
  sessionState.titleGen?.onTurnCompleted(promptText);
57866
- if (turnCompleted.turn.status === "completed" && sessionState.collaborationMode !== PLAN_COLLABORATION_MODE) sessionState.promptSuggestions?.onTurnCompleted(sessionState.sessionId, turnCompleted.turn.id);
57909
+ const promptSuggestionRequestId = sessionState.clientCapabilities.neutralPromptSuggestions ? promptSuggestionsRequestId(params._meta) : void 0;
57910
+ if (turnCompleted.turn.status === "completed" && sessionState.collaborationMode !== PLAN_COLLABORATION_MODE && (sessionState.clientCapabilities.airClient || promptSuggestionRequestId !== void 0)) {
57911
+ sessionState.promptSuggestions?.onTurnCompleted(
57912
+ sessionState.sessionId,
57913
+ turnCompleted.turn.id,
57914
+ promptSuggestionRequestId
57915
+ );
57916
+ }
57867
57917
  if (pendingInsertion === void 0) {
57868
57918
  await this.publishFallbackSessionTitle(
57869
57919
  sessionState,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.6.0-alas.1",
6
+ "version": "2.6.0-alas.2",
7
7
  "description": "",
8
8
  "main": "dist/index.js",
9
9
  "bin": {
@@ -81,6 +81,6 @@
81
81
  "alasDownstream": {
82
82
  "upstreamVersion": "2.6.0",
83
83
  "upstreamCommit": "f3208913693bd4d5171d6c6b9be5ec1ff96c6431",
84
- "sourceCommit": "7010b594e77f4f941a0712bea03aae93a8bc20dc"
84
+ "sourceCommit": "e8e45a368d0d1f832fbd5307e1c08e879d1c9de3"
85
85
  }
86
86
  }