@code-yeongyu/senpi 2026.8.21 → 2026.8.22-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 (88) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/bun-runtime.d.ts +58 -0
  3. package/dist/bun-runtime.d.ts.map +1 -0
  4. package/dist/bun-runtime.js +163 -0
  5. package/dist/bun-runtime.js.map +1 -0
  6. package/dist/cli.js +45 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/agent-session.d.ts +7 -2
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +81 -26
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/agent-settled-delivery.d.ts +13 -1
  13. package/dist/core/agent-settled-delivery.d.ts.map +1 -1
  14. package/dist/core/agent-settled-delivery.js +33 -2
  15. package/dist/core/agent-settled-delivery.js.map +1 -1
  16. package/dist/core/auth-storage.d.ts.map +1 -1
  17. package/dist/core/auth-storage.js +5 -4
  18. package/dist/core/auth-storage.js.map +1 -1
  19. package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +2 -3
  21. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  22. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +6 -0
  23. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/goal/cache-warm.js +43 -0
  25. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  26. package/dist/core/extensions/builtin/permission-system/index.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/permission-system/index.js +7 -11
  28. package/dist/core/extensions/builtin/permission-system/index.js.map +1 -1
  29. package/dist/core/model-resolver.js +2 -2
  30. package/dist/core/model-resolver.js.map +1 -1
  31. package/dist/core/session-work-barrier.d.ts.map +1 -1
  32. package/dist/core/session-work-barrier.js +38 -1
  33. package/dist/core/session-work-barrier.js.map +1 -1
  34. package/dist/modes/interactive/components/status-indicator.d.ts +3 -0
  35. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  36. package/dist/modes/interactive/components/status-indicator.js +7 -1
  37. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  38. package/dist/modes/interactive/interactive-mode.d.ts +4 -1
  39. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  40. package/dist/modes/interactive/interactive-mode.js +118 -38
  41. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  42. package/dist/senpi +45 -2
  43. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +58 -0
  44. package/node_modules/@code-yeongyu/senpi-codemode/README.md +5 -0
  45. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  46. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +4 -1
  47. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-info.ts +41 -0
  48. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +8 -3
  49. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +4 -1
  50. package/node_modules/@code-yeongyu/senpi-codemode/src/interpreters/detect.ts +17 -4
  51. package/node_modules/@code-yeongyu/senpi-codemode/src/interpreters/resolve-command.ts +68 -0
  52. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +15 -1
  53. package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/idle-timeout.ts +33 -4
  54. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +5 -1
  55. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +3 -0
  56. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +2 -0
  57. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +60 -3
  58. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/runtime-label.ts +49 -0
  59. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +13 -0
  60. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  61. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/stream-retry.d.ts +21 -0
  62. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/stream-retry.d.ts.map +1 -0
  63. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/stream-retry.js +36 -0
  64. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/stream-retry.js.map +1 -0
  65. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts +10 -0
  66. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts.map +1 -1
  67. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.js.map +1 -1
  68. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +6 -0
  69. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +166 -26
  71. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  74. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  76. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  77. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  78. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  79. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  80. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  81. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  82. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  83. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  84. package/package.json +7 -7
  85. package/dist/modes/interactive/split-trailing-assistant-text.d.ts +0 -7
  86. package/dist/modes/interactive/split-trailing-assistant-text.d.ts.map +0 -1
  87. package/dist/modes/interactive/split-trailing-assistant-text.js +0 -19
  88. package/dist/modes/interactive/split-trailing-assistant-text.js.map +0 -1
@@ -27,7 +27,7 @@ import { resolvePath } from "../utils/paths.js";
27
27
  import { sleep } from "../utils/sleep.js";
28
28
  import { normalizeToolResultImages } from "../utils/tool-result-images.js";
29
29
  import { AgentAbortProvenance } from "./agent-abort-provenance.js";
30
- import { AgentSettledDelivery } from "./agent-settled-delivery.js";
30
+ import { AgentSettledDelivery, } from "./agent-settled-delivery.js";
31
31
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
32
32
  import { executeBashWithOperations } from "./bash-executor.js";
33
33
  import { envValue } from "./brand.js";
@@ -327,6 +327,7 @@ export class AgentSession {
327
327
  this._toolExecutionDepth = 0;
328
328
  this._promptStartPending = false;
329
329
  this._nextInputId = 0;
330
+ this._settlementEpoch = 0;
330
331
  /** Tracks pending steering messages for UI display. Removed when delivered. */
331
332
  this._steeringMessages = [];
332
333
  /** Tracks pending follow-up messages for UI display. Removed when delivered. */
@@ -992,13 +993,15 @@ export class AgentSession {
992
993
  }
993
994
  this._isAgentRunActive = false;
994
995
  let deferredActions = [];
996
+ let deferredTurnClaims = [];
995
997
  this._agentSettledDelivery.begin(this._userAbortGeneration);
998
+ const settlementEpoch = ++this._settlementEpoch;
996
999
  try {
997
1000
  await this._extensionRunner.emit({ type: "agent_settled" });
998
1001
  this._emit({ type: "agent_settled" });
999
1002
  if (this._abortProvenance.takeLateUserJoin())
1000
1003
  await this._emitSessionAbort();
1001
- deferredActions = this._agentSettledDelivery.finish(this._userAbortGeneration);
1004
+ ({ actions: deferredActions, turnClaims: deferredTurnClaims } = this._agentSettledDelivery.finish(this._userAbortGeneration));
1002
1005
  }
1003
1006
  finally {
1004
1007
  this._agentSettledDelivery.cancel();
@@ -1007,8 +1010,25 @@ export class AgentSession {
1007
1010
  }
1008
1011
  for (const action of deferredActions)
1009
1012
  action();
1013
+ queueMicrotask(() => {
1014
+ void this._emitAgentIdleAfterDeferredTurns(settlementEpoch, deferredTurnClaims);
1015
+ });
1016
+ }
1017
+ async _emitAgentIdleAfterDeferredTurns(settlementEpoch, deferredTurnClaims) {
1018
+ const dispositions = await Promise.all(deferredTurnClaims.map((claim) => claim.disposition));
1019
+ if (dispositions.includes("started"))
1020
+ return;
1021
+ if (dispositions.includes("delegated") || this._sessionWorkBarrier.hasActiveWork) {
1022
+ await this._waitForSettledSessionWork();
1023
+ }
1024
+ if (settlementEpoch !== this._settlementEpoch)
1025
+ return;
1026
+ if (this._isAgentRunActive || this._sessionWorkBarrier.hasActiveWork)
1027
+ return;
1028
+ this._emit({ type: "agent_idle" });
1010
1029
  }
1011
- async _promptAgent(messages) {
1030
+ async _promptAgent(messages, deferredTurnClaim) {
1031
+ deferredTurnClaim?.resolve("started");
1012
1032
  this._isAgentRunActive = true;
1013
1033
  this._requiredCompactionAdmissionError = undefined;
1014
1034
  this.agent.abortServerSideFallback =
@@ -2908,7 +2928,7 @@ export class AgentSession {
2908
2928
  * @param options.triggerTurn If true and not streaming, triggers a new LLM turn
2909
2929
  * @param options.deliverAs Delivery mode: "steer", "followUp", or "nextTurn"
2910
2930
  */
2911
- async sendCustomMessage(message, options) {
2931
+ async sendCustomMessage(message, options, deferredTurnClaim) {
2912
2932
  const userAbortGeneration = this._userAbortGeneration;
2913
2933
  const appMessage = {
2914
2934
  role: "custom",
@@ -2941,6 +2961,7 @@ export class AgentSession {
2941
2961
  this._pendingNextTurnMessages.push(appMessage);
2942
2962
  }
2943
2963
  else if (this.isStreaming && options?.triggerTurn !== false) {
2964
+ deferredTurnClaim?.resolve("delegated");
2944
2965
  if (options?.deliverAs === "followUp") {
2945
2966
  this.agent.followUp(appMessage);
2946
2967
  }
@@ -2953,6 +2974,7 @@ export class AgentSession {
2953
2974
  (activeCompactionGeneration !== undefined &&
2954
2975
  this._compactionLifecycle.state.generation === activeCompactionGeneration &&
2955
2976
  this._compactionLifecycle.state.status !== "completed"))) {
2977
+ deferredTurnClaim?.resolve("delegated");
2956
2978
  if (options?.deliverAs === "followUp") {
2957
2979
  this.agent.followUp(appMessage);
2958
2980
  }
@@ -2978,7 +3000,7 @@ export class AgentSession {
2978
3000
  }
2979
3001
  throw error;
2980
3002
  }
2981
- await this._promptAgent(appMessage);
3003
+ await this._promptAgent(appMessage, deferredTurnClaim);
2982
3004
  }
2983
3005
  else {
2984
3006
  this.agent.state.messages.push(appMessage);
@@ -2989,6 +3011,7 @@ export class AgentSession {
2989
3011
  }
2990
3012
  }
2991
3013
  finally {
3014
+ deferredTurnClaim?.resolve("finished-without-start");
2992
3015
  finishSessionWork?.();
2993
3016
  }
2994
3017
  }
@@ -3003,7 +3026,7 @@ export class AgentSession {
3003
3026
  * @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
3004
3027
  * @param options.expandPromptTemplates Whether to dispatch extension commands and expand skill commands and prompt templates. Default: false.
3005
3028
  */
3006
- async sendUserMessage(content, options) {
3029
+ async sendUserMessage(content, options, deferredTurnClaim) {
3007
3030
  const bindingPromptReadiness = this._extensionBindingPromptReadiness;
3008
3031
  let resolveBindingPromptReadiness;
3009
3032
  if (bindingPromptReadiness) {
@@ -3012,26 +3035,35 @@ export class AgentSession {
3012
3035
  });
3013
3036
  bindingPromptReadiness.add(readiness);
3014
3037
  }
3015
- // Normalize content to text string + optional images
3038
+ // Normalize content to text string + optional images. A throw here (null or a
3039
+ // content array whose iterator/part getter throws) happens before the guarded
3040
+ // try below, so resolve the deferred-turn claim first to keep agent_idle reachable.
3016
3041
  let text;
3017
3042
  let images;
3018
- if (typeof content === "string") {
3019
- text = content;
3020
- }
3021
- else {
3022
- const textParts = [];
3023
- images = [];
3024
- for (const part of content) {
3025
- if (part.type === "text") {
3026
- textParts.push(part.text);
3027
- }
3028
- else {
3029
- images.push(part);
3043
+ try {
3044
+ if (typeof content === "string") {
3045
+ text = content;
3046
+ }
3047
+ else {
3048
+ const textParts = [];
3049
+ images = [];
3050
+ for (const part of content) {
3051
+ if (part.type === "text") {
3052
+ textParts.push(part.text);
3053
+ }
3054
+ else {
3055
+ images.push(part);
3056
+ }
3030
3057
  }
3058
+ text = textParts.join("\n");
3059
+ if (images.length === 0)
3060
+ images = undefined;
3031
3061
  }
3032
- text = textParts.join("\n");
3033
- if (images.length === 0)
3034
- images = undefined;
3062
+ }
3063
+ catch (error) {
3064
+ deferredTurnClaim?.resolve("finished-without-start");
3065
+ resolveBindingPromptReadiness?.();
3066
+ throw error;
3035
3067
  }
3036
3068
  // An extension binding invokes this method fire-and-forget. When it
3037
3069
  // arrives during session work, retain a barrier token after the prior work
@@ -3047,6 +3079,10 @@ export class AgentSession {
3047
3079
  source: "extension",
3048
3080
  promptDisposition: (nextDisposition) => {
3049
3081
  disposition = nextDisposition;
3082
+ if (nextDisposition === "started")
3083
+ deferredTurnClaim?.resolve("started");
3084
+ else if (nextDisposition === "queued")
3085
+ deferredTurnClaim?.resolve("delegated");
3050
3086
  resolveBindingPromptReadiness?.();
3051
3087
  },
3052
3088
  onSessionWorkReady: waitForExistingSessionWork
@@ -3071,6 +3107,9 @@ export class AgentSession {
3071
3107
  throw error;
3072
3108
  }
3073
3109
  finally {
3110
+ // A path that neither started nor delegated a turn (handled, rejected,
3111
+ // admission failure, cancellation) resolves as finished-without-start.
3112
+ deferredTurnClaim?.resolve("finished-without-start");
3074
3113
  resolveBindingPromptReadiness?.();
3075
3114
  finishSessionWork?.();
3076
3115
  }
@@ -4902,25 +4941,41 @@ export class AgentSession {
4902
4941
  };
4903
4942
  runner.bindCore({
4904
4943
  sendMessage: (message, options) => {
4905
- const send = () => this.sendCustomMessage(message, options).catch((err) => {
4944
+ const reportError = (err) => {
4906
4945
  runner.emitError({
4907
4946
  extensionPath: RUNTIME_EXTENSION_PATH,
4908
4947
  event: "send_message",
4909
4948
  error: err instanceof Error ? err.message : String(err),
4910
4949
  });
4911
- });
4950
+ };
4951
+ if (options?.triggerTurn === true) {
4952
+ if (this._agentSettledDelivery.deferTriggerTurn((claim) => {
4953
+ this.sendCustomMessage(message, options, claim).catch(reportError);
4954
+ })) {
4955
+ return;
4956
+ }
4957
+ }
4958
+ const send = () => this.sendCustomMessage(message, options).catch(reportError);
4912
4959
  if (this._agentSettledDelivery.defer(send))
4913
4960
  return;
4914
4961
  send();
4915
4962
  },
4916
4963
  sendUserMessage: (content, options) => {
4917
- this.sendUserMessage(content, options).catch((err) => {
4964
+ const reportError = (err) => {
4918
4965
  runner.emitError({
4919
4966
  extensionPath: RUNTIME_EXTENSION_PATH,
4920
4967
  event: "send_user_message",
4921
4968
  error: err instanceof Error ? err.message : String(err),
4922
4969
  });
4923
- });
4970
+ };
4971
+ // sendUserMessage always triggers a turn; register a settlement-deferred
4972
+ // turn claim so agent_idle is not emitted before its deferred agent_start.
4973
+ if (this._agentSettledDelivery.deferTriggerTurn((claim) => {
4974
+ this.sendUserMessage(content, options, claim).catch(reportError);
4975
+ })) {
4976
+ return;
4977
+ }
4978
+ this.sendUserMessage(content, options).catch(reportError);
4924
4979
  },
4925
4980
  appendEntry: (customType, data) => {
4926
4981
  const entryId = this.sessionManager.appendCustomEntry(customType, data);