@guanlin4924/devflow-pi 0.1.51 → 0.1.52

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -369,7 +369,7 @@ export function isRequirementDraftToolAllowed(toolName: string, input: unknown,
369
369
  return normalized === "task" && taskAgents.length > 0 && taskAgents.every(agent => PLANNING_AGENTS.has(agent));
370
370
  }
371
371
 
372
- type InternalDevflowControlResource = "contract" | "state" | "mainflow";
372
+ type InternalDevflowControlResource = "contract" | "state" | "mainflow" | "requirement_graph";
373
373
 
374
374
  /** Resolve an xdev target only when all supplied target aliases agree.
375
375
  * A direct/eval caller can otherwise provide `path` for a real file plus an
@@ -394,6 +394,7 @@ export function internalDevflowControlResource(toolName: string, input: unknown)
394
394
  if (target === "xd://devflow_contract") return "contract";
395
395
  if (target === "xd://devflow_state") return "state";
396
396
  if (target === "xd://devflow_mainflow") return "mainflow";
397
+ if (target === "xd://devflow_requirement_graph") return "requirement_graph";
397
398
  return undefined;
398
399
  }
399
400
 
@@ -1781,8 +1782,9 @@ export default function devflowCore(pi: ExtensionAPI) {
1781
1782
  if (!controlPlaneSucceeded(synced)) {
1782
1783
  throw new Error(synced.stderr || synced.stdout || "artifact registry sync after rollover prepare failed");
1783
1784
  }
1784
- await enqueueControlledRolloverRequest(ctx.cwd, rolloverId, ctx.sessionManager.getSessionId());
1785
+ // The runtime can observe the request immediately; arm the switch handler first.
1785
1786
  pendingRollover = { snapshot, preparedRevision: payload.revision, rolloverId };
1787
+ await enqueueControlledRolloverRequest(ctx.cwd, rolloverId, ctx.sessionManager.getSessionId());
1786
1788
  }
1787
1789
 
1788
1790
  function parallelBranches(snapshot: Record<string, unknown>): Array<{ workflow_step: string; track: string }> {
@@ -2850,7 +2852,7 @@ export default function devflowCore(pi: ExtensionAPI) {
2850
2852
  propagation: z.enum(["one_hop", "full"]).optional(),
2851
2853
  }).optional(),
2852
2854
  }),
2853
- approval: (args) => args.action === "propose" || args.action === "confirm" ? "write" : "read",
2855
+ approval: (args) => args.action === "propose" || args.action === "confirm" || (args.action === "context" && args.write === true) ? "write" : "read",
2854
2856
  async execute(_id, params, _signal, _onUpdate, ctx) {
2855
2857
  const graphWrite = params.action === "propose" || params.action === "confirm" || (params.action === "context" && params.write === true);
2856
2858
  if (graphWrite) {
package/src/constants.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCT_NAME = "Devflow Pi";
2
- export const PRODUCT_VERSION = "0.1.51";
2
+ export const PRODUCT_VERSION = "0.1.52";
3
3
  export const PROJECT_SCHEMA_VERSION = 1;
4
4
  export const OMP_RUNTIME_VERSION = "17.2.7";
5
5
 
package/src/runtime.ts CHANGED
@@ -209,7 +209,7 @@ export async function runInteractiveRuntime(project: ProjectContext, options: Ru
209
209
  // authority remains STATE.rollover, which the extension verifies and claims
210
210
  // during session_switch before it starts the next workflow turn.
211
211
  const consumeControlledRollover = async (): Promise<void> => {
212
- if (rolloverConsumptionInFlight) return;
212
+ if (rolloverConsumptionInFlight || interactiveSubmissionInFlight || runtime.session.isStreaming) return;
213
213
  rolloverConsumptionInFlight = true;
214
214
  try {
215
215
  const requestPath = path.join(project.root, ".planning", "rollover-request.json");
@@ -238,6 +238,8 @@ export async function runInteractiveRuntime(project: ProjectContext, options: Ru
238
238
  // already durable and STATE remains frozen until the target consumes it.
239
239
  let switched = false;
240
240
  for (let attempt = 0; attempt < 3 && !switched; attempt += 1) {
241
+ // newSession aborts a running turn; leave the durable request for the poller.
242
+ if (interactiveSubmissionInFlight || runtime.session.isStreaming) return;
241
243
  switched = await runtime.session.newSession();
242
244
  if (!switched && attempt < 2) await new Promise(resolve => setTimeout(resolve, 100));
243
245
  }
@@ -259,8 +261,11 @@ export async function runInteractiveRuntime(project: ProjectContext, options: Ru
259
261
  rolloverConsumptionInFlight = false;
260
262
  mode.showWarning(`Devflow 会话轮换运行时异常,将自动重试:${String(error)}`);
261
263
  scheduleControlledRolloverRetry();
264
+ } finally {
265
+ rolloverConsumptionInFlight = false;
262
266
  }
263
267
  };
268
+ let interactiveSubmissionInFlight = false;
264
269
  let rolloverConsumptionInFlight = false;
265
270
  let rolloverRetryTimer: ReturnType<typeof setTimeout> | undefined;
266
271
  let rolloverRetryAttempt = 0;
@@ -287,7 +292,12 @@ export async function runInteractiveRuntime(project: ProjectContext, options: Ru
287
292
  await consumeControlledRollover();
288
293
  while (true) {
289
294
  const input = await mode.getUserInput();
290
- await omp.submitInteractiveInput(mode, runtime.session, input);
295
+ interactiveSubmissionInFlight = true;
296
+ try {
297
+ await omp.submitInteractiveInput(mode, runtime.session, input);
298
+ } finally {
299
+ interactiveSubmissionInFlight = false;
300
+ }
291
301
  await consumeControlledRollover();
292
302
  // The submitted turn may create a durable rollover request after the
293
303
  // immediate check above returns. Poll once while the UI is waiting for the