@protolabsai/proto 0.47.0 → 0.47.1

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/cli.js +218 -25
  2. package/package.json +2 -2
package/cli.js CHANGED
@@ -169159,7 +169159,7 @@ __export(geminiContentGenerator_exports, {
169159
169159
  createGeminiContentGenerator: () => createGeminiContentGenerator
169160
169160
  });
169161
169161
  function createGeminiContentGenerator(config2, gcConfig) {
169162
- const version2 = "0.47.0";
169162
+ const version2 = "0.47.1";
169163
169163
  const userAgent2 = config2.userAgent || `QwenCode/${version2} (${process.platform}; ${process.arch})`;
169164
169164
  const baseHeaders = {
169165
169165
  "User-Agent": userAgent2
@@ -172165,6 +172165,8 @@ function getHookKey(hook) {
172165
172165
  identifier2 = hook.url;
172166
172166
  } else if (hook.type === HookType.Prompt) {
172167
172167
  identifier2 = hook.prompt.slice(0, 50);
172168
+ } else if (hook.type === HookType.SdkCallback) {
172169
+ identifier2 = hook.callbackId;
172168
172170
  } else {
172169
172171
  identifier2 = String(hook.type);
172170
172172
  }
@@ -172199,6 +172201,7 @@ var init_types7 = __esm({
172199
172201
  HooksConfigSource2["User"] = "user";
172200
172202
  HooksConfigSource2["System"] = "system";
172201
172203
  HooksConfigSource2["Extensions"] = "extensions";
172204
+ HooksConfigSource2["Runtime"] = "runtime";
172202
172205
  })(HooksConfigSource || (HooksConfigSource = {}));
172203
172206
  (function(HookEventName2) {
172204
172207
  HookEventName2["PreToolUse"] = "PreToolUse";
@@ -172222,6 +172225,7 @@ var init_types7 = __esm({
172222
172225
  HookType2["Command"] = "command";
172223
172226
  HookType2["Http"] = "http";
172224
172227
  HookType2["Prompt"] = "prompt";
172228
+ HookType2["SdkCallback"] = "sdkCallback";
172225
172229
  })(HookType || (HookType = {}));
172226
172230
  __name(getHookKey, "getHookKey");
172227
172231
  __name(createHookOutput, "createHookOutput");
@@ -269806,6 +269810,45 @@ var init_hookRegistry = __esm({
269806
269810
  getAllHooks() {
269807
269811
  return [...this.entries];
269808
269812
  }
269813
+ /**
269814
+ * Register a hook at runtime, bypassing the config-file load path. Used by
269815
+ * the SDK control plane (`systemController.handleInitialize`) when a host
269816
+ * process registers callback hooks via `hooks: HookRegistration[]`.
269817
+ *
269818
+ * Returns a disposer that removes the registration. Idempotent: registering
269819
+ * the same callbackId twice for the same event replaces the prior entry.
269820
+ */
269821
+ addRuntimeHook(eventName, config2, options2) {
269822
+ const hookName = this.getHookName({ config: config2 });
269823
+ this.entries = this.entries.filter((e4) => !(e4.source === HooksConfigSource.Runtime && e4.eventName === eventName && this.getHookName(e4) === hookName));
269824
+ config2.source = HooksConfigSource.Runtime;
269825
+ this.entries.push({
269826
+ config: config2,
269827
+ source: HooksConfigSource.Runtime,
269828
+ eventName,
269829
+ matcher: options2?.matcher,
269830
+ sequential: options2?.sequential,
269831
+ enabled: true
269832
+ });
269833
+ debugLogger79.debug(`Registered runtime hook "${hookName}" for ${eventName}`);
269834
+ return () => this.removeRuntimeHook(eventName, hookName);
269835
+ }
269836
+ /**
269837
+ * Remove a runtime-registered hook by event + name. No-op if not present.
269838
+ * Returns true if an entry was removed.
269839
+ */
269840
+ removeRuntimeHook(eventName, hookName) {
269841
+ const before = this.entries.length;
269842
+ this.entries = this.entries.filter((e4) => !(e4.source === HooksConfigSource.Runtime && e4.eventName === eventName && this.getHookName(e4) === hookName));
269843
+ return this.entries.length < before;
269844
+ }
269845
+ /**
269846
+ * Remove every runtime-registered hook. Used when the SDK session ends so
269847
+ * a re-init starts clean.
269848
+ */
269849
+ clearRuntimeHooks() {
269850
+ this.entries = this.entries.filter((e4) => e4.source !== HooksConfigSource.Runtime);
269851
+ }
269809
269852
  /**
269810
269853
  * Enable or disable a specific hook
269811
269854
  */
@@ -269932,7 +269975,7 @@ please review the project settings (.proto/settings.json) and remove them.`;
269932
269975
  * Validate a hook configuration
269933
269976
  */
269934
269977
  validateHookConfig(config2, eventName, source2) {
269935
- const validTypes = ["command", "http", "prompt", "plugin"];
269978
+ const validTypes = ["command", "http", "prompt", "plugin", "sdkCallback"];
269936
269979
  if (!config2.type || !validTypes.includes(config2.type)) {
269937
269980
  debugLogger79.warn(`Invalid hook ${eventName} from ${source2} type: ${config2.type}`);
269938
269981
  return false;
@@ -269949,6 +269992,10 @@ please review the project settings (.proto/settings.json) and remove them.`;
269949
269992
  debugLogger79.warn(`Prompt hook ${eventName} from ${source2} missing prompt field`);
269950
269993
  return false;
269951
269994
  }
269995
+ if (config2.type === "sdkCallback" && !("callbackId" in config2 && config2.callbackId)) {
269996
+ debugLogger79.warn(`SdkCallback hook ${eventName} from ${source2} missing callbackId`);
269997
+ return false;
269998
+ }
269952
269999
  return true;
269953
270000
  }
269954
270001
  /**
@@ -269998,6 +270045,15 @@ var init_hookRunner = __esm({
269998
270045
  static {
269999
270046
  __name(this, "HookRunner");
270000
270047
  }
270048
+ sdkCallbackInvoker;
270049
+ /**
270050
+ * Set the function used to invoke SDK-callback hooks. Called by the CLI's
270051
+ * non-interactive session once the control dispatcher is ready. Pass
270052
+ * `undefined` to clear (e.g. on session teardown).
270053
+ */
270054
+ setSdkCallbackInvoker(invoker) {
270055
+ this.sdkCallbackInvoker = invoker;
270056
+ }
270001
270057
  /**
270002
270058
  * Execute a single hook
270003
270059
  * @param hookConfig Hook configuration
@@ -270120,8 +270176,59 @@ var init_hookRunner = __esm({
270120
270176
  if (hookConfig.type === HookType.Prompt) {
270121
270177
  return this.executePromptHook(hookConfig, eventName, input, startTime);
270122
270178
  }
270179
+ if (hookConfig.type === HookType.SdkCallback) {
270180
+ return this.executeSdkCallbackHook(hookConfig, eventName, input, startTime, signal);
270181
+ }
270123
270182
  return this.executeCommandHook(hookConfig, eventName, input, startTime, signal);
270124
270183
  }
270184
+ /**
270185
+ * Execute an SDK callback hook — round-trip the event back to the host
270186
+ * process via the registered invoker and use its response as the hook
270187
+ * output. If no invoker is wired (e.g. running outside SDK mode) or the
270188
+ * host throws, treat as a non-blocking error so the agent keeps running.
270189
+ */
270190
+ async executeSdkCallbackHook(hookConfig, eventName, input, startTime, signal) {
270191
+ if (!this.sdkCallbackInvoker) {
270192
+ debugLogger80.warn(`SDK callback hook ${hookConfig.callbackId} fired for ${eventName} but no invoker is registered; skipping.`);
270193
+ return {
270194
+ hookConfig,
270195
+ eventName,
270196
+ success: true,
270197
+ duration: Date.now() - startTime
270198
+ };
270199
+ }
270200
+ const toolUseId = input.tool_use_id ?? null;
270201
+ try {
270202
+ const output = await this.sdkCallbackInvoker(hookConfig.callbackId, input, toolUseId);
270203
+ return {
270204
+ hookConfig,
270205
+ eventName,
270206
+ success: true,
270207
+ output,
270208
+ exitCode: 0,
270209
+ duration: Date.now() - startTime
270210
+ };
270211
+ } catch (error40) {
270212
+ if (signal?.aborted) {
270213
+ return {
270214
+ hookConfig,
270215
+ eventName,
270216
+ success: false,
270217
+ error: new Error("SDK callback hook cancelled (aborted)"),
270218
+ duration: Date.now() - startTime
270219
+ };
270220
+ }
270221
+ const msg = error40 instanceof Error ? error40.message : String(error40);
270222
+ debugLogger80.warn(`SDK callback hook ${hookConfig.callbackId} threw: ${msg}`);
270223
+ return {
270224
+ hookConfig,
270225
+ eventName,
270226
+ success: false,
270227
+ error: error40 instanceof Error ? error40 : new Error(msg),
270228
+ duration: Date.now() - startTime
270229
+ };
270230
+ }
270231
+ }
270125
270232
  /**
270126
270233
  * Execute an HTTP webhook hook — POST event JSON to the configured URL.
270127
270234
  */
@@ -271261,6 +271368,13 @@ var init_hookSystem = __esm({
271261
271368
  getRegistry() {
271262
271369
  return this.hookRegistry;
271263
271370
  }
271371
+ /**
271372
+ * Get the hook runner. Exposed so the SDK control plane can wire its
271373
+ * callback invoker via `getRunner().setSdkCallbackInvoker(...)`.
271374
+ */
271375
+ getRunner() {
271376
+ return this.hookRunner;
271377
+ }
271264
271378
  /**
271265
271379
  * Enable or disable a hook
271266
271380
  */
@@ -418183,7 +418297,7 @@ __name(getPackageJson, "getPackageJson");
418183
418297
  // packages/cli/src/utils/version.ts
418184
418298
  async function getCliVersion() {
418185
418299
  const pkgJson = await getPackageJson();
418186
- return "0.47.0";
418300
+ return "0.47.1";
418187
418301
  }
418188
418302
  __name(getCliVersion, "getCliVersion");
418189
418303
 
@@ -422460,6 +422574,10 @@ async function parseArguments() {
422460
422574
  type: "boolean",
422461
422575
  description: "Deprecated: use --lsp instead. This flag is ignored.",
422462
422576
  hidden: true
422577
+ }).option("experimental-hooks", {
422578
+ type: "boolean",
422579
+ description: "Accept SDK hook registrations on the control plane. Set automatically by @protolabsai/sdk when hookCallbacks is provided.",
422580
+ hidden: true
422463
422581
  }).option("lsp", {
422464
422582
  type: "boolean",
422465
422583
  description: "Enable LSP (Language Server Protocol) for code intelligence (goToDefinition, diagnostics, etc.)",
@@ -426191,7 +426309,7 @@ var formatDuration = /* @__PURE__ */ __name((milliseconds) => {
426191
426309
 
426192
426310
  // packages/cli/src/generated/git-commit.ts
426193
426311
  init_esbuild_shims();
426194
- var GIT_COMMIT_INFO = "ca3b7f2d7";
426312
+ var GIT_COMMIT_INFO = "2fb5fc6c1";
426195
426313
 
426196
426314
  // packages/cli/src/utils/systemInfo.ts
426197
426315
  async function getNpmVersion() {
@@ -439217,6 +439335,7 @@ var ControlContext = class {
439217
439335
  sdkMcpServers;
439218
439336
  mcpClients;
439219
439337
  inputClosed;
439338
+ pendingHookRegistrations;
439220
439339
  onInterrupt;
439221
439340
  constructor(options2) {
439222
439341
  this.config = options2.config;
@@ -439228,6 +439347,7 @@ var ControlContext = class {
439228
439347
  this.sdkMcpServers = /* @__PURE__ */ new Set();
439229
439348
  this.mcpClients = /* @__PURE__ */ new Map();
439230
439349
  this.inputClosed = false;
439350
+ this.pendingHookRegistrations = [];
439231
439351
  this.onInterrupt = options2.onInterrupt;
439232
439352
  }
439233
439353
  };
@@ -439499,6 +439619,14 @@ var SystemController = class extends BaseController {
439499
439619
  }
439500
439620
  }
439501
439621
  }
439622
+ if (payload.hooks && Array.isArray(payload.hooks)) {
439623
+ this.context.pendingHookRegistrations = payload.hooks;
439624
+ debugLogger128.debug(
439625
+ `[SystemController] Stashed ${payload.hooks.length} hook registration(s) for finalization`
439626
+ );
439627
+ } else {
439628
+ this.context.pendingHookRegistrations = [];
439629
+ }
439502
439630
  if (payload.agents && Array.isArray(payload.agents)) {
439503
439631
  try {
439504
439632
  this.context.config.setSessionSubagents(payload.agents);
@@ -439532,7 +439660,7 @@ var SystemController = class extends BaseController {
439532
439660
  buildControlCapabilities() {
439533
439661
  const capabilities = {
439534
439662
  can_handle_can_use_tool: true,
439535
- can_handle_hook_callback: false,
439663
+ can_handle_hook_callback: true,
439536
439664
  can_set_permission_mode: typeof this.context.config.setApprovalMode === "function",
439537
439665
  can_set_model: typeof this.context.config.setModel === "function",
439538
439666
  // SDK MCP servers are supported - messages routed through control plane
@@ -440662,6 +440790,56 @@ var Session2 = class {
440662
440790
  throw error40;
440663
440791
  }
440664
440792
  }
440793
+ /**
440794
+ * Wire any hook registrations the SDK sent in INITIALIZE into the now-built
440795
+ * HookSystem, and set the round-trip invoker so SdkCallback hooks call back
440796
+ * to the SDK process via the control plane. No-op when not in SDK mode or
440797
+ * when the host sent no registrations.
440798
+ */
440799
+ finalizeHookRegistrations() {
440800
+ if (!this.controlContext || !this.dispatcher) return;
440801
+ const pending = this.controlContext.pendingHookRegistrations ?? [];
440802
+ const hookSystem = this.config.getHookSystem?.();
440803
+ if (!hookSystem) {
440804
+ if (pending.length > 0) {
440805
+ debugLogger130.warn(
440806
+ "[Session] SDK sent hook registrations but the HookSystem is disabled (disableAllHooks?); hooks will not fire."
440807
+ );
440808
+ }
440809
+ return;
440810
+ }
440811
+ const dispatcher = this.dispatcher;
440812
+ hookSystem.getRunner().setSdkCallbackInvoker(async (callbackId, input, toolUseId) => {
440813
+ const response = await dispatcher.sendControlRequest({
440814
+ subtype: "hook_callback",
440815
+ callback_id: callbackId,
440816
+ input,
440817
+ tool_use_id: toolUseId
440818
+ });
440819
+ if (response.subtype !== "success") return void 0;
440820
+ return response.response;
440821
+ });
440822
+ const registry3 = hookSystem.getRegistry();
440823
+ registry3.clearRuntimeHooks();
440824
+ for (const reg of pending) {
440825
+ if (!Object.values(HookEventName).includes(reg.event)) {
440826
+ debugLogger130.warn(
440827
+ `[Session] Ignoring SDK hook registration for unknown event "${reg.event}"`
440828
+ );
440829
+ continue;
440830
+ }
440831
+ registry3.addRuntimeHook(reg.event, {
440832
+ type: HookType.SdkCallback,
440833
+ callbackId: reg.callback_id,
440834
+ name: reg.callback_id
440835
+ });
440836
+ }
440837
+ if (pending.length > 0) {
440838
+ debugLogger130.debug(
440839
+ `[Session] Wired ${pending.length} SDK hook registration(s)`
440840
+ );
440841
+ }
440842
+ }
440665
440843
  /**
440666
440844
  * Mark initialization as complete
440667
440845
  */
@@ -440759,6 +440937,7 @@ var Session2 = class {
440759
440937
  await this.dispatcher?.dispatch(request3);
440760
440938
  const sendSdkMcpMessage = this.dispatcher?.sdkMcpController.createSendSdkMcpMessage();
440761
440939
  await this.ensureConfigInitialized({ sendSdkMcpMessage });
440940
+ this.finalizeHookRegistrations();
440762
440941
  this.completeInitialization();
440763
440942
  } catch (error40) {
440764
440943
  debugLogger130.error("[Session] SDK mode initialization failed:", error40);
@@ -474398,7 +474577,8 @@ function getTranslatedSourceDisplayMap() {
474398
474577
  [HooksConfigSource.Project]: t4("Local Settings"),
474399
474578
  [HooksConfigSource.User]: t4("User Settings"),
474400
474579
  [HooksConfigSource.System]: t4("System Settings"),
474401
- [HooksConfigSource.Extensions]: t4("Extensions")
474580
+ [HooksConfigSource.Extensions]: t4("Extensions"),
474581
+ [HooksConfigSource.Runtime]: t4("SDK (Runtime)")
474402
474582
  };
474403
474583
  }
474404
474584
  __name(getTranslatedSourceDisplayMap, "getTranslatedSourceDisplayMap");
@@ -479728,54 +479908,67 @@ var Footer = /* @__PURE__ */ __name(() => {
479728
479908
  // packages/cli/src/ui/components/StickyTaskList.tsx
479729
479909
  init_esbuild_shims();
479730
479910
  var import_jsx_runtime129 = __toESM(require_jsx_runtime(), 1);
479731
- var MAX_OPEN_DISPLAY = 7;
479911
+ var MAX_PENDING_DISPLAY = 7;
479732
479912
  var COMPLETED_PREVIEW = 2;
479733
479913
  var StickyTaskList = /* @__PURE__ */ __name(({ todos }) => {
479734
479914
  if (!todos || todos.length === 0) {
479735
479915
  return null;
479736
479916
  }
479737
- const open6 = todos.filter((t5) => t5.status !== "completed");
479917
+ const inProgress = todos.filter((t5) => t5.status === "in_progress");
479918
+ const pending = todos.filter((t5) => t5.status === "pending");
479738
479919
  const completed = todos.filter((t5) => t5.status === "completed");
479739
479920
  const total = todos.length;
479740
- const openShown = open6.slice(0, MAX_OPEN_DISPLAY);
479741
- const openHidden = open6.length - openShown.length;
479921
+ const pendingShown = pending.slice(0, MAX_PENDING_DISPLAY);
479922
+ const pendingHidden = pending.length - pendingShown.length;
479742
479923
  const completedShown = completed.slice(0, COMPLETED_PREVIEW);
479743
479924
  const completedHidden = completed.length - completedShown.length;
479744
- return /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
479925
+ return /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [
479745
479926
  /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Text3, { color: theme.text.secondary, children: [
479746
- total,
479747
- " task",
479927
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { bold: true, color: theme.text.primary, children: total }),
479928
+ " ",
479929
+ "task",
479748
479930
  total === 1 ? "" : "s",
479749
479931
  " (",
479750
- completed.length,
479751
- " done,",
479932
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { bold: true, color: theme.text.primary, children: completed.length }),
479933
+ " ",
479934
+ "done,",
479935
+ " ",
479936
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { bold: true, color: theme.text.primary, children: inProgress.length }),
479937
+ " ",
479938
+ "in progress,",
479939
+ " ",
479940
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { bold: true, color: theme.text.primary, children: pending.length }),
479752
479941
  " ",
479753
- open6.length,
479754
- " open)"
479942
+ "open)"
479755
479943
  ] }),
479756
- openShown.map((todo) => /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(TaskRow, { todo }, todo.id)),
479757
- openHidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Text3, { color: theme.ui.comment, children: [
479944
+ inProgress.map((todo) => /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(TaskRow, { todo }, todo.id)),
479945
+ pendingShown.map((todo) => /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(TaskRow, { todo }, todo.id)),
479946
+ pendingHidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Text3, { color: theme.ui.comment, children: [
479758
479947
  " \u2026 +",
479759
- openHidden,
479760
- " more open"
479948
+ pendingHidden,
479949
+ " pending"
479761
479950
  ] }),
479762
479951
  completedShown.map((todo) => /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(TaskRow, { todo }, todo.id)),
479763
479952
  completedHidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Text3, { color: theme.ui.comment, children: [
479764
479953
  " \u2026 +",
479765
479954
  completedHidden,
479766
- " completed"
479955
+ " done"
479767
479956
  ] })
479768
479957
  ] });
479769
479958
  }, "StickyTaskList");
479770
479959
  var TaskRow = /* @__PURE__ */ __name(({ todo }) => {
479771
479960
  const isCompleted = todo.status === "completed";
479772
479961
  const isInProgress = todo.status === "in_progress";
479962
+ const glyph = isCompleted ? "\u2714" : isInProgress ? "\u25A0" : "\u25A1";
479963
+ const glyphColor = isCompleted ? theme.status.success : isInProgress ? theme.status.warning : theme.ui.comment;
479964
+ const textColor = isCompleted ? theme.ui.comment : isInProgress ? theme.text.primary : theme.text.secondary;
479773
479965
  return /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(Box_default, { flexDirection: "row", children: [
479774
- /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Box_default, { width: 2, children: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { color: isCompleted ? theme.status.success : theme.ui.comment, children: isCompleted ? "\u2714" : "\u25FB" }) }),
479966
+ /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Box_default, { marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Text3, { color: glyphColor, children: glyph }) }),
479775
479967
  /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
479776
479968
  Text3,
479777
479969
  {
479778
- color: isCompleted ? theme.ui.comment : isInProgress ? theme.status.success : theme.text.primary,
479970
+ bold: isInProgress,
479971
+ color: textColor,
479779
479972
  strikethrough: isCompleted,
479780
479973
  wrap: "truncate-end",
479781
479974
  children: todo.content
@@ -496470,7 +496663,7 @@ var QwenAgent = class {
496470
496663
  async initialize(args2) {
496471
496664
  this.clientCapabilities = args2.clientCapabilities;
496472
496665
  const authMethods = buildAuthMethods();
496473
- const version2 = "0.47.0";
496666
+ const version2 = "0.47.1";
496474
496667
  return {
496475
496668
  protocolVersion: PROTOCOL_VERSION,
496476
496669
  agentInfo: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protolabsai/proto",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "proto - AI-powered coding agent",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,7 @@
21
21
  "bundled"
22
22
  ],
23
23
  "config": {
24
- "sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.47.0"
24
+ "sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.47.1"
25
25
  },
26
26
  "dependencies": {},
27
27
  "optionalDependencies": {