@akagilnc/pi-workflow-roles 0.1.3789 → 0.1.3797

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.
@@ -90,6 +90,17 @@ export class CollectorNonOpenRequestError extends CorrectableSubmissionError {
90
90
  this.name = "CollectorNonOpenRequestError";
91
91
  }
92
92
  }
93
+ /** #678: wait window not open or already ended — bounce without latching fatal so materials still seal. */
94
+ export class CollectorWaitWindowClosedError extends CorrectableSubmissionError {
95
+ constructor(action) {
96
+ super(action === "request"
97
+ ? "通进司请求不在资格截止前"
98
+ : action === "wait-before-open"
99
+ ? "通进司等待需要先在工作步骤开启等待窗(新建 PR 用创建成功时刻;已有 PR 在触发阶段结束后开启)"
100
+ : "通进司等待不在资格截止前");
101
+ this.name = "CollectorWaitWindowClosedError";
102
+ }
103
+ }
93
104
  function candidateRecord(candidate) {
94
105
  if (candidate === undefined || candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
95
106
  return undefined;
@@ -1,11 +1,13 @@
1
- import { applyEvidenceVersionHistory, assignWindowRelations, COLLECTOR_ELIGIBILITY_MS, measureNormalizedBytes, normalizeAuthenticatedUserEvidence, normalizeIssueCommentEvidence, normalizePullRequestEvidence, normalizePullRequestReactionEvidence, normalizeReviewCommentEvidence, normalizeReviewEvidence, sha256Text, } from "./collector-evidence.js";
1
+ import { applyEvidenceVersionHistory, assignWindowRelations, COLLECTOR_DEFAULT_WAIT_WINDOW_MS, measureNormalizedBytes, normalizeAuthenticatedUserEvidence, normalizeIssueCommentEvidence, normalizePullRequestEvidence, normalizePullRequestReactionEvidence, normalizeReviewCommentEvidence, normalizeReviewEvidence, sha256Text, } from "./collector-evidence.js";
2
2
  import { buildCollectorRequestBody, } from "./collector-github.js";
3
- import { CollectorNonOpenRequestError } from "./collector-identity.js";
3
+ import { CollectorNonOpenRequestError, CollectorWaitWindowClosedError, } from "./collector-identity.js";
4
4
  import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.js";
5
5
  export const COLLECTOR_OBSERVE_TOOL = "ak_collector_observe";
6
6
  export const COLLECTOR_READ_TOOL = "ak_collector_read";
7
7
  export const COLLECTOR_REQUEST_TOOL = "ak_collector_request";
8
8
  export const COLLECTOR_WAIT_TOOL = "ak_collector_wait";
9
+ /** #678 D4: open wait window at a work step — business tool, ledger-booked. */
10
+ export const COLLECTOR_OPEN_WAIT_WINDOW_TOOL = "ak_collector_open_wait_window";
9
11
  /** #676 A: role-decided target bind — business tool, ledger-booked. */
10
12
  export const COLLECTOR_BIND_TARGET_TOOL = "ak_collector_bind_target";
11
13
  /** #677: opaque handbook write — business tool, ledger-booked. */
@@ -16,6 +18,7 @@ export const COLLECTOR_OPERATIONAL_TOOLS = [
16
18
  COLLECTOR_OBSERVE_TOOL,
17
19
  COLLECTOR_READ_TOOL,
18
20
  COLLECTOR_REQUEST_TOOL,
21
+ COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
19
22
  COLLECTOR_WAIT_TOOL,
20
23
  COLLECTOR_HANDBOOK_WRITE_TOOL,
21
24
  ];
@@ -57,13 +60,28 @@ const COLLECTOR_WAIT_ENTRY_TYPE = "ak-collector-wait";
57
60
  function isOperationalTool(name) {
58
61
  return COLLECTOR_OPERATIONAL_TOOLS.includes(name);
59
62
  }
60
- export function createCollectorLedger(config, options) {
63
+ function resolveWaitWindowMs(raw) {
64
+ if (raw === undefined)
65
+ return COLLECTOR_DEFAULT_WAIT_WINDOW_MS;
66
+ if (!Number.isSafeInteger(raw) || raw < 1) {
67
+ throw new Error("通进司 waitWindowMs 须为正安全整数毫秒");
68
+ }
69
+ return raw;
70
+ }
71
+ export function createCollectorLedger(input, options) {
72
+ const config = {
73
+ repository: input.repository,
74
+ prNumber: input.prNumber,
75
+ manifest: input.manifest,
76
+ waitWindowMs: resolveWaitWindowMs(input.waitWindowMs),
77
+ };
61
78
  const clock = options?.clock;
62
79
  const journal = options?.journal;
63
80
  let fatal = false;
64
81
  let fatalReason;
65
82
  let outputCandidate = false;
66
83
  let pendingOutputCallId;
84
+ let sessionReady = false;
67
85
  let activationTime;
68
86
  let deadlineTime;
69
87
  let activationMono;
@@ -110,7 +128,7 @@ export function createCollectorLedger(config, options) {
110
128
  };
111
129
  const remainingMs = (clock) => {
112
130
  if (deadlineMono === undefined)
113
- return COLLECTOR_ELIGIBILITY_MS;
131
+ return config.waitWindowMs;
114
132
  return Math.max(0, deadlineMono - monoNowOrThrow(clock));
115
133
  };
116
134
  const prIdentity = (pr) => `${pr.state}|${pr.headOid}|${pr.updatedAt ?? ""}`;
@@ -181,8 +199,10 @@ export function createCollectorLedger(config, options) {
181
199
  return copy;
182
200
  };
183
201
  const commitActivationWindow = (nextActivation, nextDeadline) => {
202
+ // D6: once open, PR updates / second open must not reset the window.
184
203
  if (activationTime !== undefined)
185
204
  return;
205
+ sessionReady = true;
186
206
  activationTime = nextActivation;
187
207
  deadlineTime = nextDeadline;
188
208
  bindDeadlineMonoFromWall();
@@ -310,6 +330,9 @@ export function createCollectorLedger(config, options) {
310
330
  if (typeof data.activationTime === "string" && typeof data.deadlineTime === "string") {
311
331
  commitActivationWindow(new Date(data.activationTime), new Date(data.deadlineTime));
312
332
  }
333
+ else if (data.sessionReady === true) {
334
+ sessionReady = true;
335
+ }
313
336
  continue;
314
337
  }
315
338
  if (entry.customType === COLLECTOR_SNAPSHOT_ENTRY_TYPE) {
@@ -370,7 +393,7 @@ export function createCollectorLedger(config, options) {
370
393
  return outputCandidate || pendingOutputCallId !== undefined;
371
394
  },
372
395
  get activationRecorded() {
373
- return activationTime !== undefined;
396
+ return sessionReady;
374
397
  },
375
398
  get activationTime() {
376
399
  return activationTime;
@@ -407,17 +430,39 @@ export function createCollectorLedger(config, options) {
407
430
  },
408
431
  latchFatal,
409
432
  assertNotFatal,
410
- recordActivation(clock) {
433
+ recordActivation(_clock) {
411
434
  assertNotFatal();
412
- if (activationTime !== undefined)
435
+ if (sessionReady)
413
436
  return;
414
- activationTime = clock.wallNow();
415
- activationMono = clock.monoNow();
416
- deadlineTime = new Date(activationTime.getTime() + COLLECTOR_ELIGIBILITY_MS);
417
- deadlineMono = activationMono + COLLECTOR_ELIGIBILITY_MS;
437
+ sessionReady = true;
438
+ // Session ready only — wait window opens later at a work step (#678 D4).
439
+ appendJournal(COLLECTOR_ACTIVATION_ENTRY_TYPE, {
440
+ sessionReady: true,
441
+ });
442
+ },
443
+ openWaitWindow(clock, options) {
444
+ assertNotFatal();
445
+ if (!sessionReady) {
446
+ throw latchFatal("通进司开启等待窗需要先激活会话");
447
+ }
448
+ if (activationTime !== undefined)
449
+ return; // D6: do not reset
450
+ const startedAt = options?.startedAt ?? clock.wallNow();
451
+ if (!(startedAt instanceof Date) || Number.isNaN(startedAt.getTime())) {
452
+ throw new Error("通进司等待窗 startedAt 须为有效时间");
453
+ }
454
+ const nextDeadline = new Date(startedAt.getTime() + config.waitWindowMs);
455
+ // Bind mono relative to current clock so a past startedAt shortens remaining correctly.
456
+ activationTime = startedAt;
457
+ deadlineTime = nextDeadline;
458
+ const wallNow = clock.wallNow().getTime();
459
+ const monoNow = clock.monoNow();
460
+ activationMono = monoNow - (wallNow - startedAt.getTime());
461
+ deadlineMono = monoNow + (nextDeadline.getTime() - wallNow);
418
462
  appendJournal(COLLECTOR_ACTIVATION_ENTRY_TYPE, {
419
463
  activationTime: activationTime.toISOString(),
420
464
  deadlineTime: deadlineTime.toISOString(),
465
+ waitWindowMs: config.waitWindowMs,
421
466
  });
422
467
  },
423
468
  recordOutputCandidate() {
@@ -502,7 +547,7 @@ export function createCollectorLedger(config, options) {
502
547
  },
503
548
  async observe(transport, clock, signal) {
504
549
  assertNotFatal();
505
- if (activationTime === undefined) {
550
+ if (!sessionReady) {
506
551
  throw latchFatal("通进司观察需要激活");
507
552
  }
508
553
  if (signal?.aborted) {
@@ -592,6 +637,7 @@ export function createCollectorLedger(config, options) {
592
637
  prNumber: requireBoundPr(),
593
638
  prState: pr.state,
594
639
  headOid: pr.headOid,
640
+ ...(pr.createdAt === undefined ? {} : { prCreatedAt: pr.createdAt }),
595
641
  complete: true,
596
642
  evidenceIds: storedIds,
597
643
  pageDiagnostics,
@@ -625,7 +671,7 @@ export function createCollectorLedger(config, options) {
625
671
  },
626
672
  async request(input, transport, clock, signal) {
627
673
  assertNotFatal();
628
- if (activationTime === undefined || deadlineTime === undefined) {
674
+ if (!sessionReady) {
629
675
  throw latchFatal("通进司请求需要激活");
630
676
  }
631
677
  if (ledger.unresolvedTransportFailure) {
@@ -662,7 +708,8 @@ export function createCollectorLedger(config, options) {
662
708
  }
663
709
  if (pastCutoff(clock)) {
664
710
  finalObservationRequired = true;
665
- throw latchFatal("通进司请求不在资格截止前");
711
+ // #678: do not latch fatal — materials must still seal after the window ends.
712
+ throw new CollectorWaitWindowClosedError("request");
666
713
  }
667
714
  // Caller manifest body wins when requestId is configured; otherwise role body.
668
715
  const configuredBody = configured?.requestBody ?? roleBody;
@@ -766,18 +813,22 @@ export function createCollectorLedger(config, options) {
766
813
  },
767
814
  async wait(input, clock, signal) {
768
815
  assertNotFatal();
769
- if (activationTime === undefined) {
816
+ if (!sessionReady) {
770
817
  throw latchFatal("通进司等待需要激活");
771
818
  }
819
+ // #678: wait never invents a window start. Work-step open (create-success / trigger-end)
820
+ // must be explicit — silent "now" would break the new-PR create-success clock.
821
+ if (activationTime === undefined || deadlineTime === undefined) {
822
+ throw new CollectorWaitWindowClosedError("wait-before-open");
823
+ }
772
824
  // durationMs shape authority = collectorWaitArgsSchema (host parameters).
773
825
  if (pastCutoff(clock)) {
774
826
  finalObservationRequired = true;
775
- throw latchFatal("通进司等待不在资格截止前");
827
+ throw new CollectorWaitWindowClosedError("wait");
776
828
  }
777
829
  const remaining = remainingMs(clock);
778
- // Single-wait runtime cadence cap (v2 §6 / §9); schema max stays 15m.
779
- const COLLECTOR_SINGLE_WAIT_MAX_MS = 300_000;
780
- const effectiveMs = Math.min(input.durationMs, remaining, COLLECTOR_SINGLE_WAIT_MAX_MS);
830
+ // Cap only by remaining window — no package-local single-wait ceiling (ADR 0035 / #678).
831
+ const effectiveMs = Math.min(input.durationMs, remaining);
781
832
  const startedAt = clock.wallNow().toISOString();
782
833
  const waitId = sha256Text(`wait:${startedAt}:${effectiveMs}`).slice(0, 16);
783
834
  await clock.sleep(effectiveMs, signal);
@@ -860,6 +911,9 @@ function buildObserveModelView(input) {
860
911
  completedAt: input.snapshot.completedAt,
861
912
  prState: input.snapshot.prState,
862
913
  headOid: input.snapshot.headOid,
914
+ ...(input.snapshot.prCreatedAt === undefined
915
+ ? {}
916
+ : { prCreatedAt: input.snapshot.prCreatedAt }),
863
917
  complete: input.snapshot.complete,
864
918
  evidence: relevant.map((record) => projectEvidenceEntryView(record)),
865
919
  requestAttempts: input.attempts.map((attempt) => ({
@@ -1,10 +1,10 @@
1
1
  import { emptyCollectorManifest, loadCollectorManifest, parseCollectorPrNumber, parseCollectorRepository, } from "./collector-config.js";
2
- import { createSystemCollectorClock, } from "./collector-evidence.js";
2
+ import { COLLECTOR_DEFAULT_WAIT_WINDOW_MS, createSystemCollectorClock, } from "./collector-evidence.js";
3
3
  import { createGhApiRunner, listPullRequestNumbersByTicket, } from "./collector-github.js";
4
4
  import { createCollectorHandbookStore, resolveCollectorHandbookRoot, } from "./collector-handbook.js";
5
- import { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_HANDBOOK_WRITE_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, projectEvidenceEntryView, } from "./collector-ledger.js";
5
+ import { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_HANDBOOK_WRITE_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OPEN_WAIT_WINDOW_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, projectEvidenceEntryView, } from "./collector-ledger.js";
6
6
  import { buildCollectorReceipt, } from "./collector-receipt.js";
7
- import { collectorBindTargetArgsSchema, collectorHandbookWriteArgsSchema, collectorObserveArgsSchema, collectorOutputArgsSchema, collectorReadArgsSchema, collectorRequestArgsSchema, collectorWaitArgsSchema, } from "./collector-tool-schemas.js";
7
+ import { collectorBindTargetArgsSchema, collectorHandbookWriteArgsSchema, collectorObserveArgsSchema, collectorOpenWaitWindowArgsSchema, collectorOutputArgsSchema, collectorReadArgsSchema, collectorRequestArgsSchema, collectorWaitArgsSchema, } from "./collector-tool-schemas.js";
8
8
  import { COLLECTOR_ACCEPTED_TEXT } from "./package-contracts/collector-output.js";
9
9
  import { CorrectableSubmissionError, isCorrectableExecuteError } from "./submission-correctable-error.js";
10
10
  import { CollectorUnknownEvidenceError } from "./collector-identity.js";
@@ -27,12 +27,13 @@ export class CollectorTargetBindError extends CorrectableSubmissionError {
27
27
  this.name = "CollectorTargetBindError";
28
28
  }
29
29
  }
30
- export { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_HANDBOOK_WRITE_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, };
30
+ export { COLLECTOR_BIND_TARGET_TOOL, COLLECTOR_HANDBOOK_WRITE_TOOL, COLLECTOR_OBSERVE_TOOL, COLLECTOR_OPEN_WAIT_WINDOW_TOOL, COLLECTOR_OUTPUT_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, };
31
31
  export const COLLECTOR_REQUIRED_TOOLS = [
32
32
  COLLECTOR_BIND_TARGET_TOOL,
33
33
  COLLECTOR_OBSERVE_TOOL,
34
34
  COLLECTOR_READ_TOOL,
35
35
  COLLECTOR_REQUEST_TOOL,
36
+ COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
36
37
  COLLECTOR_WAIT_TOOL,
37
38
  COLLECTOR_HANDBOOK_WRITE_TOOL,
38
39
  COLLECTOR_OUTPUT_TOOL,
@@ -60,14 +61,34 @@ export const COLLECTOR_TRANSPORT_FLAGS = Object.freeze([
60
61
  type: "string",
61
62
  }),
62
63
  }),
64
+ Object.freeze({
65
+ name: "ak-collector-wait-ms",
66
+ definition: Object.freeze({
67
+ description: `Collector wait-window duration in milliseconds (default ${COLLECTOR_DEFAULT_WAIT_WINDOW_MS}). Caller-configurable; opens at a work step, not session start (#678 D4).`,
68
+ type: "string",
69
+ }),
70
+ }),
63
71
  ]);
64
72
  const observeSchema = collectorObserveArgsSchema;
65
73
  const readSchema = collectorReadArgsSchema;
66
74
  const requestSchema = collectorRequestArgsSchema;
75
+ const openWaitWindowSchema = collectorOpenWaitWindowArgsSchema;
67
76
  const waitSchema = collectorWaitArgsSchema;
68
77
  const bindSchema = collectorBindTargetArgsSchema;
69
78
  const handbookWriteSchema = collectorHandbookWriteArgsSchema;
70
79
  const outputSchema = collectorOutputArgsSchema;
80
+ function parseWaitWindowMsFlag(raw) {
81
+ if (raw === undefined || raw === null || raw === "")
82
+ return COLLECTOR_DEFAULT_WAIT_WINDOW_MS;
83
+ if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1)
84
+ return raw;
85
+ if (typeof raw === "string" && /^[1-9]\d*$/.test(raw.trim())) {
86
+ const value = Number(raw.trim());
87
+ if (Number.isSafeInteger(value) && value >= 1)
88
+ return value;
89
+ }
90
+ throw new Error("Collector --ak-collector-wait-ms must be a positive safe-integer millisecond string");
91
+ }
71
92
  function buildMethodContext(activation) {
72
93
  const pr = activation.ledger.config.prNumber;
73
94
  const handbook = activation.handbookView;
@@ -76,6 +97,7 @@ function buildMethodContext(activation) {
76
97
  `host: github.com`,
77
98
  `repository: ${activation.repository.canonical}`,
78
99
  `prNumber: ${pr === undefined ? "未绑定" : String(pr)}`,
100
+ `waitWindowMs: ${String(activation.ledger.config.waitWindowMs)}`,
79
101
  `requests: ${JSON.stringify(activation.manifest.requests.map((request) => ({ id: request.id })))}`,
80
102
  `handbookGeneralSource: ${handbook.generalSource}`,
81
103
  `handbookRepoSource: ${handbook.repoSource}`,
@@ -119,6 +141,7 @@ export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
119
141
  const repoFlag = pi.getFlag("ak-collector-repo");
120
142
  const prFlag = pi.getFlag("ak-collector-pr");
121
143
  const requestManifestFlag = pi.getFlag("ak-collector-request-manifest");
144
+ const waitMsFlag = pi.getFlag("ak-collector-wait-ms");
122
145
  if (typeof repoFlag !== "string" || repoFlag.trim().length === 0) {
123
146
  throw new Error("Collector requires --ak-collector-repo");
124
147
  }
@@ -134,9 +157,10 @@ export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
134
157
  const manifest = typeof requestManifestFlag === "string" && requestManifestFlag.trim().length > 0
135
158
  ? await loadCollectorManifest(requestManifestFlag)
136
159
  : emptyCollectorManifest();
160
+ const waitWindowMs = parseWaitWindowMsFlag(waitMsFlag);
137
161
  const clock = dependencies.createClock?.() ?? createSystemCollectorClock();
138
162
  const transport = dependencies.createTransport();
139
- const ledger = dependencies.createLedger({ repository, prNumber, manifest }, clock, ctx);
163
+ const ledger = dependencies.createLedger({ repository, prNumber, manifest, waitWindowMs }, clock, ctx);
140
164
  // #677: handbook under admitted book topology (session path → books/<key>/collector-handbook).
141
165
  const sessionPath = ctx.sessionManager?.getSessionFile?.()
142
166
  ?? ctx.sessionManager?.getSessionDir?.();
@@ -412,11 +436,60 @@ export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
412
436
  }
413
437
  },
414
438
  });
439
+ pi.registerTool({
440
+ name: COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
441
+ label: "通进司开启等待窗",
442
+ description: "在工作步骤上开启等待窗:新建并自动触发的 PR 传创建成功时刻;已有 PR 在触发阶段结束后省略 startedAt(=现在)。窗长由使用方配置,默认十分钟;开启后不因 PR 更新重置。",
443
+ promptSnippet: "按工作步骤开启等待窗",
444
+ parameters: openWaitWindowSchema,
445
+ async execute(toolCallId, params, _signal, _onUpdate, ctx) {
446
+ const activation = getActivation();
447
+ if (activation === undefined)
448
+ throw new Error("通进司未激活");
449
+ try {
450
+ activation.ledger.beginOperational(COLLECTOR_OPEN_WAIT_WINDOW_TOOL, toolCallId);
451
+ let startedAt;
452
+ if (typeof params.startedAt === "string" && params.startedAt.trim().length > 0) {
453
+ const parsed = new Date(params.startedAt);
454
+ if (Number.isNaN(parsed.getTime())) {
455
+ throw new Error("通进司等待窗 startedAt 须为有效 ISO 时间");
456
+ }
457
+ startedAt = parsed;
458
+ }
459
+ activation.ledger.openWaitWindow(activation.clock, startedAt === undefined ? undefined : { startedAt });
460
+ activation.ledger.completeOperational(toolCallId);
461
+ return {
462
+ content: [{
463
+ type: "text",
464
+ text: "等待窗已开启",
465
+ }],
466
+ details: {
467
+ activationTime: activation.ledger.activationTime?.toISOString(),
468
+ deadlineTime: activation.ledger.deadlineTime?.toISOString(),
469
+ waitWindowMs: activation.ledger.config.waitWindowMs,
470
+ },
471
+ };
472
+ }
473
+ catch (error) {
474
+ if (isCorrectableExecuteError(error))
475
+ throw error;
476
+ hostActions.failInfrastructure(error, ctx, toolCallId);
477
+ }
478
+ finally {
479
+ try {
480
+ activation.ledger.completeOperational(toolCallId);
481
+ }
482
+ catch {
483
+ // already completed or not begun
484
+ }
485
+ }
486
+ },
487
+ });
415
488
  pi.registerTool({
416
489
  name: COLLECTOR_WAIT_TOOL,
417
490
  label: "通进司等待",
418
- description: "再观察前等待;单次上限五分钟且不超剩余资格。",
419
- promptSnippet: "资格截止前等待",
491
+ description: "再观察前等待;实际睡眠不超过剩余等待窗。",
492
+ promptSnippet: "等待窗内等待",
420
493
  parameters: waitSchema,
421
494
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
422
495
  const activation = getActivation();
@@ -435,6 +508,8 @@ export function createCollectorRoleRuntime(pi, dependencies, hostActions) {
435
508
  };
436
509
  }
437
510
  catch (error) {
511
+ if (isCorrectableExecuteError(error))
512
+ throw error;
438
513
  hostActions.failInfrastructure(error, ctx, toolCallId);
439
514
  }
440
515
  },
@@ -1,5 +1,4 @@
1
1
  import { Type } from "typebox";
2
- import { COLLECTOR_ELIGIBILITY_MS } from "./collector-evidence.js";
3
2
  import { openToolObject } from "./open-tool-schema.js";
4
3
  import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.js";
5
4
  export const collectorObserveArgsSchema = Type.Object({}, { additionalProperties: false });
@@ -35,7 +34,21 @@ export const collectorReadArgsSchema = Type.Object({
35
34
  evidenceId: Type.String({ minLength: 1, description: "observe 返回的材料证据 id(evidenceId)" }),
36
35
  }, { additionalProperties: false });
37
36
  export const collectorWaitArgsSchema = Type.Object({
38
- durationMs: Type.Integer({ minimum: 1, maximum: COLLECTOR_ELIGIBILITY_MS, description: "等待毫秒;单次上限五分钟且不超剩余资格" }),
37
+ durationMs: Type.Integer({
38
+ minimum: 1,
39
+ description: "等待毫秒;实际睡眠不超过剩余等待窗(#678;无包内单次任意上限)",
40
+ }),
41
+ }, { additionalProperties: false });
42
+ /**
43
+ * #678 D4: open the wait window at a work step.
44
+ * Omit startedAt for existing-PR trigger-phase end (= now).
45
+ * Pass PR creation success time for new-PR auto-trigger rounds.
46
+ */
47
+ export const collectorOpenWaitWindowArgsSchema = Type.Object({
48
+ startedAt: Type.Optional(Type.String({
49
+ minLength: 1,
50
+ description: "等待窗起点 ISO 时间;新建 PR 用创建成功时刻;省略=现在(触发阶段结束)",
51
+ })),
39
52
  }, { additionalProperties: false });
40
53
  /**
41
54
  * #676 A: role-decided target bind. The model judges task materials and submits