@danypops/pi-jittor 0.7.0 → 0.8.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.
Files changed (2) hide show
  1. package/extension/src/index.ts +123 -152
  2. package/package.json +2 -2
@@ -164,21 +164,6 @@ async function refreshFooter(client: JittorExtensionClient, state: IntegratedFoo
164
164
  state.requestRender?.();
165
165
  }
166
166
 
167
- function delay(milliseconds: number, signal?: AbortSignal): Promise<void> {
168
- if (milliseconds <= 0) return Promise.resolve();
169
- return new Promise((resolve, reject) => {
170
- const timer = setTimeout(resolve, milliseconds);
171
- signal?.addEventListener(
172
- "abort",
173
- () => {
174
- clearTimeout(timer);
175
- reject(new Error("Jittor throttle cancelled"));
176
- },
177
- { once: true },
178
- );
179
- });
180
- }
181
-
182
167
  function routeModelAvailable(ctx: ExtensionContext, route: Route): boolean {
183
168
  return ctx.modelRegistry.getAvailable().some((model) => model.provider === route.provider && model.id === route.model);
184
169
  }
@@ -318,38 +303,6 @@ async function syncCurrentRoute(
318
303
  });
319
304
  }
320
305
 
321
- function halt(ctx: ExtensionContext, reason: string): false {
322
- ctx.ui.notify(`${reason}. ${RECOVERY_GUIDANCE}.`, "warning");
323
- ctx.abort();
324
- return false;
325
- }
326
-
327
- async function applyDecision(
328
- pi: ExtensionAPI,
329
- client: JittorExtensionClient,
330
- ctx: ExtensionContext,
331
- decision: PolicyDecision,
332
- allowResync = true,
333
- ): Promise<boolean> {
334
- if (decision.action === "halt") return halt(ctx, `Jittor blocked this provider request: ${decision.reason}`);
335
- if (decision.action === "throttle") await delay(decision.delayMs ?? 0, ctx.signal);
336
- if (!decision.route || (await applyRoute(pi, ctx, decision.route))) return true;
337
- if (allowResync) {
338
- await syncAvailableRoutes(pi, client, ctx);
339
- return applyDecision(
340
- pi,
341
- client,
342
- ctx,
343
- (await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() })) as PolicyDecision,
344
- false,
345
- );
346
- }
347
- return halt(
348
- ctx,
349
- `Jittor could not apply any authenticated Pi route after ${decision.route.provider}/${decision.route.model} became unavailable`,
350
- );
351
- }
352
-
353
306
  let assistantUsageRunSequence = 0;
354
307
 
355
308
  /**
@@ -503,54 +456,61 @@ export function registerJittorExtension(
503
456
  const contextFingerprinter = new HmacContextFingerprinter(contextFingerprintKey);
504
457
  let contextCaptureSequence = 0;
505
458
  pi.on("before_provider_request", (event, ctx) => {
506
- try {
507
- const sessionId = ctx.sessionManager.getSessionId();
508
- let history:
509
- | {
510
- roots: SessionTreeNodeLike[];
511
- activeEntryIds: Set<string>;
512
- branchEntryIds: Set<string>;
513
- }
514
- | undefined;
459
+ // Pi awaits this hook immediately before opening the provider request. Snapshotting a large
460
+ // payload here used to add its full CPU cost to TTFT even though persistence was detached.
461
+ // Defer the entire observational path to a later macrotask; the provider payload is immutable
462
+ // after serialization in Pi's request path, and a missed snapshot is preferable to delaying it.
463
+ const timer = setTimeout(() => {
515
464
  try {
516
- history = {
517
- roots: ctx.sessionManager.getTree() as SessionTreeNodeLike[],
518
- activeEntryIds: new Set((ctx.sessionManager.buildContextEntries() as SessionEntryLike[]).map((entry) => entry.id)),
519
- branchEntryIds: new Set((ctx.sessionManager.getBranch() as SessionEntryLike[]).map((entry) => entry.id)),
465
+ const sessionId = ctx.sessionManager.getSessionId();
466
+ let history:
467
+ | {
468
+ roots: SessionTreeNodeLike[];
469
+ activeEntryIds: Set<string>;
470
+ branchEntryIds: Set<string>;
471
+ }
472
+ | undefined;
473
+ try {
474
+ history = {
475
+ roots: ctx.sessionManager.getTree() as SessionTreeNodeLike[],
476
+ activeEntryIds: new Set((ctx.sessionManager.buildContextEntries() as SessionEntryLike[]).map((entry) => entry.id)),
477
+ branchEntryIds: new Set((ctx.sessionManager.getBranch() as SessionEntryLike[]).map((entry) => entry.id)),
478
+ };
479
+ } catch {
480
+ // Older/custom SessionManager implementations may not expose tree projections.
481
+ }
482
+ const captureInput = {
483
+ payload: event.payload,
484
+ captureId: `${++contextCaptureSequence}`,
485
+ sessionId,
486
+ provider: ctx.model?.provider ?? "unknown",
487
+ model: ctx.model?.id ?? "unknown",
488
+ capturedAt: Date.now(),
489
+ fingerprinter: contextFingerprinter,
520
490
  };
491
+ let snapshot: ContextSnapshot;
492
+ try {
493
+ snapshot = captureProviderContextSnapshot({ ...captureInput, ...(history ? { history } : {}) });
494
+ } catch {
495
+ // A custom SessionManager tree shape must not suppress the real request-payload snapshot.
496
+ snapshot = captureProviderContextSnapshot(captureInput);
497
+ }
498
+ // Observation must never alter or abort the provider request. Both local writes are detached;
499
+ // they receive only bounded token sizes and keyed fingerprints.
500
+ const requestTokens = snapshot.segments
501
+ .filter((segment) => segment.requestPosition !== null)
502
+ .reduce((sum, segment) => sum + segment.tokens, 0);
503
+ const compactionMetrics = compactionTelemetry.observeContextSnapshot(requestTokens, "structural-estimate", snapshot.capturedAt, {
504
+ provider: snapshot.provider,
505
+ model: snapshot.model,
506
+ });
507
+ void client.call("context.snapshot", snapshot).catch(() => undefined);
508
+ if (compactionMetrics.length > 0) void recordMetrics(client, compactionMetrics).catch(() => undefined);
521
509
  } catch {
522
- // Older/custom SessionManager implementations may not expose tree projections.
523
- }
524
- const captureInput = {
525
- payload: event.payload,
526
- captureId: `${++contextCaptureSequence}`,
527
- sessionId,
528
- provider: ctx.model?.provider ?? "unknown",
529
- model: ctx.model?.id ?? "unknown",
530
- capturedAt: Date.now(),
531
- fingerprinter: contextFingerprinter,
532
- };
533
- let snapshot: ContextSnapshot;
534
- try {
535
- snapshot = captureProviderContextSnapshot({ ...captureInput, ...(history ? { history } : {}) });
536
- } catch {
537
- // A custom SessionManager tree shape must not suppress the real request-payload snapshot.
538
- snapshot = captureProviderContextSnapshot(captureInput);
510
+ // Snapshot collection is strictly failure-isolated from provider delivery.
539
511
  }
540
- // Observation must never alter or abort the provider request. Both local writes are detached;
541
- // they receive only bounded token sizes and keyed fingerprints.
542
- const requestTokens = snapshot.segments
543
- .filter((segment) => segment.requestPosition !== null)
544
- .reduce((sum, segment) => sum + segment.tokens, 0);
545
- const compactionMetrics = compactionTelemetry.observeContextSnapshot(requestTokens, "structural-estimate", snapshot.capturedAt, {
546
- provider: snapshot.provider,
547
- model: snapshot.model,
548
- });
549
- void client.call("context.snapshot", snapshot).catch(() => undefined);
550
- if (compactionMetrics.length > 0) void recordMetrics(client, compactionMetrics).catch(() => undefined);
551
- } catch {
552
- // Snapshot collection is strictly failure-isolated from provider delivery.
553
- }
512
+ }, 0);
513
+ timer.unref?.();
554
514
  });
555
515
  const stopContextHub = pi.events?.on?.(CONTEXT_HUB_CONTRIBUTION_CHANNEL, (payload) => contextHub.observe(payload));
556
516
  // Cached from the most recent before_agent_start observation: Pi's own base system prompt is
@@ -1002,25 +962,58 @@ export function registerJittorExtension(
1002
962
  ]).catch(() => undefined);
1003
963
  });
1004
964
 
1005
- pi.on("agent_settled", async (_event, ctx) => {
965
+ let ambientRouting: Promise<void> | null = null;
966
+ const scheduleAmbientRouting = (ctx: ExtensionContext): void => {
967
+ if (ambientRouting || !ctx.isIdle()) return;
968
+ ambientRouting = (async () => {
969
+ try {
970
+ await syncCurrentRoute(pi, client, ctx);
971
+ await syncAvailableRoutes(pi, client, ctx);
972
+ if (!ctx.isIdle()) return;
973
+ let budgetActed = false;
974
+ if (enforcement.isEnabled()) {
975
+ const decision = (await client.call("router.decide", {
976
+ session_id: ctx.sessionManager.getSessionId(),
977
+ })) as PolicyDecision;
978
+ if (!ctx.isIdle()) return;
979
+ budgetActed = decision.action !== "continue";
980
+ if (decision.action === "halt") {
981
+ ctx.ui.notify(`Jittor recommends pausing future requests: ${decision.reason}.`, "warning");
982
+ } else if (decision.route && !(await applyRoute(pi, ctx, decision.route)) && ctx.isIdle()) {
983
+ // Catalogs can change while an ambient decision is in flight. Refresh and try one
984
+ // newly-computed route; failure simply leaves the current model in place.
985
+ await syncAvailableRoutes(pi, client, ctx);
986
+ const refreshed = (await client.call("router.decide", {
987
+ session_id: ctx.sessionManager.getSessionId(),
988
+ })) as PolicyDecision;
989
+ if (ctx.isIdle() && refreshed.route) await applyRoute(pi, ctx, refreshed.route);
990
+ }
991
+ }
992
+ if (!budgetActed && ctx.isIdle()) await runAutoModeTurn(ctx);
993
+ } catch {
994
+ // Routing is opportunistic: daemon latency or failure must never enter Pi's request path.
995
+ } finally {
996
+ ambientRouting = null;
997
+ }
998
+ })();
999
+ };
1000
+
1001
+ pi.on("agent_settled", (_event, ctx) => {
1006
1002
  if (footerState.compaction) {
1007
1003
  finishCompactionUi();
1008
1004
  if (compactionTelemetry.hasOpenCompaction())
1009
- await recordMetrics(client, [compactionTelemetry.abort(Date.now(), "agent-settled-without-completion")]).catch(() => undefined);
1005
+ void recordMetrics(client, [compactionTelemetry.abort(Date.now(), "agent-settled-without-completion")]).catch(() => undefined);
1010
1006
  }
1011
1007
  scheduleCodexRecovery(ctx);
1008
+ scheduleAmbientRouting(ctx);
1012
1009
  if (!enforcement.isFooterEnabled()) return;
1013
- try {
1014
- await syncCurrentRoute(pi, client, ctx);
1015
- await syncAvailableRoutes(pi, client, ctx);
1016
- await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
1017
- } catch {
1010
+ void refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => {
1018
1011
  footerState.providerBudget = null;
1019
1012
  footerState.requestRender?.();
1020
- }
1013
+ });
1021
1014
  });
1022
1015
 
1023
- pi.on("input", async (event, ctx) => {
1016
+ pi.on("input", (event) => {
1024
1017
  if (event.source !== "extension") {
1025
1018
  cancelRecovery(true);
1026
1019
  // Auto mode's own effort classifier reads this at the next turn_start -- captured
@@ -1028,57 +1021,34 @@ export function registerJittorExtension(
1028
1021
  // separate, independently-toggled feature from budget enforcement.
1029
1022
  pendingUserText = event.text;
1030
1023
  }
1031
- if (event.source === "extension" || !enforcement.isEnabled()) return { action: "continue" as const };
1032
- try {
1033
- const next = (await client.call("router.decide", { session_id: ctx.sessionManager.getSessionId() })) as PolicyDecision;
1034
- if (next.action === "halt") {
1035
- ctx.ui.notify(`Jittor blocked input: ${next.reason}. ${RECOVERY_GUIDANCE}.`, "warning");
1036
- return { action: "handled" as const };
1037
- }
1038
- return { action: "continue" as const };
1039
- } catch {
1040
- ctx.ui.notify(`Jittor could not verify budget telemetry, so fail-closed enforcement blocked input. ${RECOVERY_GUIDANCE}.`, "error");
1041
- return { action: "handled" as const };
1042
- }
1024
+ // Routing is advisory and ambient. The submitted message always enters Pi immediately;
1025
+ // daemon decisions computed while idle can optimize a later turn.
1026
+ return { action: "continue" as const };
1043
1027
  });
1044
1028
 
1045
- pi.on("model_select", async (event, ctx) => {
1046
- await syncCurrentRoute(pi, client, ctx, event.model)
1029
+ pi.on("model_select", (event, ctx) => {
1030
+ void syncCurrentRoute(pi, client, ctx, event.model)
1047
1031
  .then(() => syncAvailableRoutes(pi, client, ctx))
1032
+ .then(() =>
1033
+ enforcement.isFooterEnabled()
1034
+ ? refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined)
1035
+ : undefined,
1036
+ )
1048
1037
  .catch(() => undefined);
1049
- if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
1050
1038
  });
1051
1039
 
1052
- pi.on("thinking_level_select", async (event, ctx) => {
1053
- await syncCurrentRoute(pi, client, ctx, ctx.model, event.level).catch(() => undefined);
1040
+ pi.on("thinking_level_select", (event, ctx) => {
1041
+ void syncCurrentRoute(pi, client, ctx, ctx.model, event.level).catch(() => undefined);
1054
1042
  });
1055
1043
 
1056
- pi.on("turn_start", async (event, ctx) => {
1044
+ pi.on("turn_start", (event, ctx) => {
1057
1045
  currentSessionId = ctx.sessionManager.getSessionId();
1058
1046
  compactionTelemetry.observeTurn();
1059
1047
  codexRecoveryCapability.resetTurn();
1060
1048
  providerResponseTelemetry.resetTurn();
1061
1049
  localRunTelemetry.beginTurn(event.timestamp);
1062
- // Budget/enforcement routing is safety-critical and always wins on conflict: Auto mode is
1063
- // only evaluated when budget pressure required no action this turn (action === "continue"),
1064
- // so the two decision sources never fight over the model mid-turn.
1065
- let budgetActedThisTurn = false;
1066
- if (enforcement.isEnabled()) {
1067
- try {
1068
- await syncCurrentRoute(pi, client, ctx);
1069
- await syncAvailableRoutes(pi, client, ctx);
1070
- const budgetDecision = (await client.call("router.decide", {
1071
- session_id: ctx.sessionManager.getSessionId(),
1072
- })) as PolicyDecision;
1073
- budgetActedThisTurn = budgetDecision.action !== "continue";
1074
- await applyDecision(pi, client, ctx, budgetDecision);
1075
- await refreshFooter(client, footerState, ctx.sessionManager.getSessionId());
1076
- } catch {
1077
- halt(ctx, "Jittor could not verify or apply a safe route");
1078
- return;
1079
- }
1080
- }
1081
- if (!budgetActedThisTurn) await runAutoModeTurn(ctx).catch(() => undefined);
1050
+ // Deliberately no daemon or routing work here: Pi awaits turn_start before provider delivery.
1051
+ // The previous agent_settled event computes and applies recommendations for later turns.
1082
1052
  });
1083
1053
 
1084
1054
  pi.on("message_update", async (event) => {
@@ -1095,35 +1065,35 @@ export function registerJittorExtension(
1095
1065
  if (currentTurnToolNames.length < EFFORT_CLASSIFICATION_MAX_TOOL_NAMES) currentTurnToolNames.push(event.toolName);
1096
1066
  });
1097
1067
 
1098
- pi.on("after_provider_response", async (event, ctx) => {
1068
+ pi.on("after_provider_response", (event, ctx) => {
1099
1069
  localRunTelemetry.onProviderResponse();
1100
1070
  if (ctx.model?.provider === "openai-codex") codexRecoveryCapability.notifyResponse(event.status, event.headers);
1101
1071
  const notifySchemaDrift = (message: string) => {
1102
1072
  if (enforcement.isEnabled()) ctx.ui.notify(`Jittor detected ${message}. ${RECOVERY_GUIDANCE}.`, "error");
1103
1073
  };
1104
- await providerResponseTelemetry.handleProviderResponse(client, ctx.model?.provider, event.status, event.headers, notifySchemaDrift);
1105
- if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
1074
+ // Pi fires this before consuming the provider stream. Never put telemetry or footer RPCs
1075
+ // between the HTTP response and the first streamed token.
1076
+ void providerResponseTelemetry
1077
+ .handleProviderResponse(client, ctx.model?.provider, event.status, event.headers, notifySchemaDrift)
1078
+ .catch(() => undefined);
1106
1079
  });
1107
1080
 
1108
- pi.on("turn_end", async (event, ctx) => {
1081
+ pi.on("turn_end", (event, ctx) => {
1109
1082
  const tokens = ctx.getContextUsage()?.tokens;
1110
1083
  if (typeof tokens === "number" && Number.isFinite(tokens)) contextGrowth.observe(++contextGrowthTurn, tokens);
1111
1084
  const metrics = localRunTelemetry.completeTurn(event.message, pi.getThinkingLevel());
1112
- await recordMetrics(client, metrics).catch(() => undefined);
1085
+ void recordMetrics(client, metrics).catch(() => undefined);
1113
1086
  priorTurnToolNames = currentTurnToolNames;
1114
1087
  currentTurnToolNames = [];
1115
1088
  });
1116
1089
 
1117
- pi.on("message_end", async (event, ctx) => {
1090
+ pi.on("message_end", (event, ctx) => {
1118
1091
  if (event.message.role === "assistant") {
1119
1092
  if (event.message.provider === "openai-codex")
1120
1093
  codexRecoveryCapability.notifyMessageEnd(event.message.stopReason, event.message.errorMessage);
1121
- await providerResponseTelemetry.handleMessageEnd(
1122
- client,
1123
- event.message.provider,
1124
- event.message.stopReason,
1125
- event.message.errorMessage,
1126
- );
1094
+ void providerResponseTelemetry
1095
+ .handleMessageEnd(client, event.message.provider, event.message.stopReason, event.message.errorMessage)
1096
+ .catch(() => undefined);
1127
1097
  }
1128
1098
  const metrics = assistantUsageMetrics(
1129
1099
  event.message,
@@ -1143,9 +1113,10 @@ export function registerJittorExtension(
1143
1113
  cacheRead: amount("cache-read-tokens"),
1144
1114
  cacheWrite: amount("cache-write-tokens"),
1145
1115
  });
1146
- await recordMetrics(client, metrics).catch(() => undefined);
1116
+ void recordMetrics(client, metrics).catch(() => undefined);
1147
1117
  }
1148
- if (enforcement.isFooterEnabled()) await refreshFooter(client, footerState, ctx.sessionManager.getSessionId()).catch(() => undefined);
1118
+ // Footer refresh is consolidated in agent_settled; finalized-message accounting must not
1119
+ // hold back streamed lifecycle delivery or the next queued prompt.
1149
1120
  });
1150
1121
 
1151
1122
  pi.on("session_shutdown", async (_event, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-jittor",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Pi extension for Jittor token and context observability with optimization controls backed by the @danypops/jittor daemon",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@danypops/pi-extension-harness": "^0.6.2",
29
- "@danypops/pi-process-harness": "^0.1.2",
29
+ "@danypops/pi-process-harness": "^0.3.2",
30
30
  "@danypops/pi-tui-harness": "^0.0.2",
31
31
  "@earendil-works/pi-ai": "*",
32
32
  "bun-types": "latest"