@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.
- package/README.md +3 -1
- package/README.zh-CN.md +3 -1
- package/dist/acp-host/production-host.js +203 -42
- package/dist/collector-evidence.js +4 -1
- package/dist/collector-github.js +1 -0
- package/dist/collector-identity.js +11 -0
- package/dist/collector-ledger.js +73 -19
- package/dist/collector-role.js +82 -7
- package/dist/collector-tool-schemas.js +15 -2
- package/dist/headless-host/production-host.js +203 -42
- package/dist/pi/role-turn-host.js +1 -0
- package/dist/public-cli/invocation.js +15 -0
- package/dist/public-cli/main.js +43 -5
- package/dist/public-cli/option-definitions.js +14 -0
- package/dist/public-cli/run-lifecycle.js +8 -0
- package/package.json +1 -1
- package/souls/countersign.md +12 -10
- package/src/acp-host/role-envelope.ts +1 -0
- package/src/collector-evidence.ts +6 -1
- package/src/collector-github.ts +3 -0
- package/src/collector-identity.ts +14 -0
- package/src/collector-ledger.ts +95 -23
- package/src/collector-role.ts +81 -3
- package/src/collector-tool-schemas.ts +17 -2
- package/src/host-contracts.ts +2 -0
- package/src/pi/role-turn-host.ts +1 -0
- package/src/public-cli/collector-run.ts +2 -0
- package/src/public-cli/invocation.ts +21 -0
- package/src/public-cli/option-definitions.ts +14 -0
- package/src/public-cli/run-lifecycle.ts +10 -0
|
@@ -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;
|
package/dist/collector-ledger.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { applyEvidenceVersionHistory, assignWindowRelations,
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
433
|
+
recordActivation(_clock) {
|
|
411
434
|
assertNotFatal();
|
|
412
|
-
if (
|
|
435
|
+
if (sessionReady)
|
|
413
436
|
return;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
|
827
|
+
throw new CollectorWaitWindowClosedError("wait");
|
|
776
828
|
}
|
|
777
829
|
const remaining = remainingMs(clock);
|
|
778
|
-
//
|
|
779
|
-
const
|
|
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) => ({
|
package/dist/collector-role.js
CHANGED
|
@@ -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({
|
|
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
|