@fieldwangai/agentflow 0.1.135 → 0.1.136
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/bin/lib/ui-server.mjs +300 -37
- package/bin/lib/workflow-report.mjs +70 -9
- package/builtin/web-ui/dist/assets/{index-CmpbCHAj.js → index-HdswcJWY.js} +278 -133
- package/builtin/web-ui/dist/assets/index-KIGufzQf.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/agentflow-cli/SKILL.md +1 -1
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +24 -4
- package/skills/agentflow-cli/scripts/workflow-report-client.mjs +68 -0
- package/skills/agentflow-workflow-report/SKILL.md +31 -8
- package/skills/agentflow-workflow-report/references/protocol.md +457 -236
- package/builtin/web-ui/dist/assets/index-BFQVTav-.css +0 -1
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -176,6 +176,7 @@ import {
|
|
|
176
176
|
} from "./teams.mjs";
|
|
177
177
|
import {
|
|
178
178
|
legacyOverallToGlobalState,
|
|
179
|
+
materializeWorkflowExtensions,
|
|
179
180
|
materializeWorkflowGlobalState,
|
|
180
181
|
materializeWorkflowProjections,
|
|
181
182
|
mergeWorkflowArtifactLists,
|
|
@@ -7997,7 +7998,17 @@ function prdWorkflowSafeStateId(value) {
|
|
|
7997
7998
|
}
|
|
7998
7999
|
|
|
7999
8000
|
function prdWorkflowReviewIdFromRequest(tapdId, payload = {}, durability = "temporary") {
|
|
8000
|
-
if (durability === "temporary")
|
|
8001
|
+
if (durability === "temporary") {
|
|
8002
|
+
const idempotencyKey = String(payload.idempotencyKey || payload.idempotency_key || "").trim();
|
|
8003
|
+
if (!idempotencyKey) return `r-${crypto.randomBytes(5).toString("hex")}`;
|
|
8004
|
+
const source = String(payload.source || "agentflow-cli").trim().toLowerCase() || "agentflow-cli";
|
|
8005
|
+
const digest = crypto
|
|
8006
|
+
.createHash("sha256")
|
|
8007
|
+
.update(JSON.stringify({ tapdId: String(tapdId || ""), source, idempotencyKey }))
|
|
8008
|
+
.digest("hex")
|
|
8009
|
+
.slice(0, 12);
|
|
8010
|
+
return `r-${digest}`;
|
|
8011
|
+
}
|
|
8001
8012
|
const requested = String(payload.reviewId || payload.review_id || "").trim();
|
|
8002
8013
|
if (!requested) return `review_${Date.now().toString(36)}_${crypto.randomBytes(4).toString("hex")}`;
|
|
8003
8014
|
const safeRequested = prdWorkflowSafeStateId(requested);
|
|
@@ -10171,6 +10182,89 @@ function prdWorkflowSnapshotMetaFromReport(payload = {}, rawSnapshot = {}, req =
|
|
|
10171
10182
|
};
|
|
10172
10183
|
}
|
|
10173
10184
|
|
|
10185
|
+
function prdWorkflowStoreClientObservation({
|
|
10186
|
+
scopedRoot,
|
|
10187
|
+
tapdId,
|
|
10188
|
+
rawState,
|
|
10189
|
+
payload = {},
|
|
10190
|
+
req = null,
|
|
10191
|
+
userCtx = {},
|
|
10192
|
+
flowSource = "user",
|
|
10193
|
+
flowId = "",
|
|
10194
|
+
}) {
|
|
10195
|
+
const normalizedSnapshot = {
|
|
10196
|
+
...prdWorkflowSnapshotFromParsed(scopedRoot, tapdId, rawState, userCtx, { flowSource, flowId }),
|
|
10197
|
+
clientReportedAt: new Date().toISOString(),
|
|
10198
|
+
sources: {
|
|
10199
|
+
...(rawState.sources && typeof rawState.sources === "object" ? rawState.sources : {}),
|
|
10200
|
+
executionMode: "workflow-report",
|
|
10201
|
+
},
|
|
10202
|
+
};
|
|
10203
|
+
const reportMeta = prdWorkflowSnapshotMetaFromReport(payload, rawState, req, userCtx);
|
|
10204
|
+
const reportSource = {
|
|
10205
|
+
...(normalizedSnapshot.sources && typeof normalizedSnapshot.sources === "object" ? normalizedSnapshot.sources : {}),
|
|
10206
|
+
executionMode: "workflow-report",
|
|
10207
|
+
truth: "observation",
|
|
10208
|
+
authority: "client",
|
|
10209
|
+
persistence: "runtime",
|
|
10210
|
+
clientId: reportMeta.clientId,
|
|
10211
|
+
clientUserId: reportMeta.userId,
|
|
10212
|
+
clientReportedAt: reportMeta.reportedAt,
|
|
10213
|
+
clientObservedAt: reportMeta.observedAt,
|
|
10214
|
+
baseRevision: reportMeta.baseRevision,
|
|
10215
|
+
scope: reportMeta.scope,
|
|
10216
|
+
platform: reportMeta.platform,
|
|
10217
|
+
issueKey: reportMeta.issueKey,
|
|
10218
|
+
stageKey: reportMeta.stageKey,
|
|
10219
|
+
};
|
|
10220
|
+
const existingClientState = prdWorkflowReadClientState(scopedRoot, tapdId);
|
|
10221
|
+
const existingClientId = prdWorkflowSafeStateId(reportMeta.clientId || "anonymous");
|
|
10222
|
+
const previousClientSnapshot = existingClientState.clients?.[existingClientId]?.snapshot || null;
|
|
10223
|
+
const stampedSnapshot = prdWorkflowStampCurrentActionEntryTimes(
|
|
10224
|
+
scopedRoot,
|
|
10225
|
+
tapdId,
|
|
10226
|
+
normalizedSnapshot,
|
|
10227
|
+
existingClientState,
|
|
10228
|
+
reportMeta,
|
|
10229
|
+
);
|
|
10230
|
+
const storedObservationSnapshot = prdWorkflowStoredObservationSnapshot(stampedSnapshot, reportSource);
|
|
10231
|
+
const actionChanges = prdWorkflowSnapshotActionChanges(previousClientSnapshot || {}, storedObservationSnapshot);
|
|
10232
|
+
prdWorkflowWriteClientObservation(scopedRoot, tapdId, reportMeta, storedObservationSnapshot);
|
|
10233
|
+
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
10234
|
+
type: "workflow-report-observation-stored",
|
|
10235
|
+
flowSource,
|
|
10236
|
+
flowId,
|
|
10237
|
+
clientId: reportMeta.clientId,
|
|
10238
|
+
userId: reportMeta.userId,
|
|
10239
|
+
observedAt: reportMeta.observedAt,
|
|
10240
|
+
reportedAt: reportMeta.reportedAt,
|
|
10241
|
+
phase: String(storedObservationSnapshot?.phase || ""),
|
|
10242
|
+
pointer: String(storedObservationSnapshot?.pointer || ""),
|
|
10243
|
+
revision: String(storedObservationSnapshot?.revision || ""),
|
|
10244
|
+
actionCount: prdWorkflowSnapshotActionCount(storedObservationSnapshot),
|
|
10245
|
+
truth: "observation",
|
|
10246
|
+
authority: "client",
|
|
10247
|
+
persistence: "runtime",
|
|
10248
|
+
note: "producer observation accepted through the canonical Workflow Report endpoint",
|
|
10249
|
+
});
|
|
10250
|
+
for (const change of actionChanges) {
|
|
10251
|
+
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
10252
|
+
type: "snapshot-action-change",
|
|
10253
|
+
source: "workflow-report",
|
|
10254
|
+
clientId: reportMeta.clientId,
|
|
10255
|
+
userId: reportMeta.userId,
|
|
10256
|
+
observedAt: reportMeta.observedAt,
|
|
10257
|
+
reportedAt: reportMeta.reportedAt,
|
|
10258
|
+
revision: String(storedObservationSnapshot.revision || ""),
|
|
10259
|
+
previousRevision: String(previousClientSnapshot?.revision || ""),
|
|
10260
|
+
pointer: String(storedObservationSnapshot.pointer || ""),
|
|
10261
|
+
previousPointer: String(previousClientSnapshot?.pointer || ""),
|
|
10262
|
+
...change,
|
|
10263
|
+
});
|
|
10264
|
+
}
|
|
10265
|
+
return { reportMeta, storedObservationSnapshot, previousClientSnapshot };
|
|
10266
|
+
}
|
|
10267
|
+
|
|
10174
10268
|
function prdWorkflowSnapshotReportConflict(existingRecord, incomingSnapshot, meta) {
|
|
10175
10269
|
if (!existingRecord?.snapshot || meta.force) return null;
|
|
10176
10270
|
const current = existingRecord.snapshot;
|
|
@@ -10565,6 +10659,13 @@ function prdWorkflowRuntimeEventCanonicalAction(stage = "") {
|
|
|
10565
10659
|
: "";
|
|
10566
10660
|
}
|
|
10567
10661
|
|
|
10662
|
+
function prdWorkflowRuntimeEventProducer(event = {}) {
|
|
10663
|
+
return String(event.source || event.producer || "agentflow")
|
|
10664
|
+
.trim()
|
|
10665
|
+
.toLowerCase()
|
|
10666
|
+
.slice(0, 120) || "agentflow";
|
|
10667
|
+
}
|
|
10668
|
+
|
|
10568
10669
|
function prdWorkflowRuntimeOwnedArtifacts(values, stage = "") {
|
|
10569
10670
|
if (!Array.isArray(values)) return values;
|
|
10570
10671
|
const ownsOnlyChangedArtifact = /^(?:issue-plan|implementation|bugfix|integration):/.test(String(stage || ""));
|
|
@@ -10588,14 +10689,14 @@ function prdWorkflowRuntimeEventId(event = {}) {
|
|
|
10588
10689
|
const aggregateByStage = event.aggregateByStage !== false && event.aggregate_by_stage !== false;
|
|
10589
10690
|
if ((stage || action) && aggregateByStage) {
|
|
10590
10691
|
const issue = String(event.issueKey || event.issue_key || event.issue || "").trim();
|
|
10591
|
-
const key = [scope, issue, platform, stage || action].filter(Boolean).join(":");
|
|
10692
|
+
const key = [prdWorkflowRuntimeEventProducer(event), scope, issue, platform, stage || action].filter(Boolean).join(":");
|
|
10592
10693
|
return `stage_${prdWorkflowSafeStateId(key)}`;
|
|
10593
10694
|
}
|
|
10594
10695
|
const existing = String(event.id || event.eventId || event.event_id || "").trim();
|
|
10595
10696
|
if (existing) return existing.slice(0, 160);
|
|
10596
10697
|
if (stage || action) {
|
|
10597
10698
|
const issue = String(event.issueKey || event.issue_key || event.issue || "").trim();
|
|
10598
|
-
const key = [scope, issue, platform, stage || action].filter(Boolean).join(":");
|
|
10699
|
+
const key = [prdWorkflowRuntimeEventProducer(event), scope, issue, platform, stage || action].filter(Boolean).join(":");
|
|
10599
10700
|
return `stage_${prdWorkflowSafeStateId(key)}`;
|
|
10600
10701
|
}
|
|
10601
10702
|
return `evt_${Date.now().toString(36)}_${crypto.randomBytes(4).toString("hex")}`;
|
|
@@ -10659,8 +10760,13 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
|
|
|
10659
10760
|
entry.stage = stage;
|
|
10660
10761
|
entry.stageKey = stage;
|
|
10661
10762
|
}
|
|
10662
|
-
|
|
10663
|
-
|
|
10763
|
+
const attachProducer = (values) => (Array.isArray(values) ? values.map((item) => (
|
|
10764
|
+
item && typeof item === "object" && !Array.isArray(item)
|
|
10765
|
+
? { ...item, producer: String(item.producer || source).trim().toLowerCase() || source }
|
|
10766
|
+
: item
|
|
10767
|
+
)) : values);
|
|
10768
|
+
entry.artifacts = attachProducer(prdWorkflowRuntimeOwnedArtifacts(entry.artifacts, stage));
|
|
10769
|
+
entry.links = attachProducer(prdWorkflowRuntimeOwnedArtifacts(entry.links, stage));
|
|
10664
10770
|
if (scope) entry.scope = scope;
|
|
10665
10771
|
if (platform) entry.platform = platform;
|
|
10666
10772
|
if (!entry.createdAt) entry.createdAt = event.startedAt || now;
|
|
@@ -10759,8 +10865,12 @@ function prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, event = {}) {
|
|
|
10759
10865
|
const current = prdWorkflowReadRuntimeEvents(scopedRoot, tapdId);
|
|
10760
10866
|
const entry = prdWorkflowNormalizeRuntimeEvent(tapdId, event);
|
|
10761
10867
|
const entryIdem = String(entry.idempotencyKey || "").trim();
|
|
10868
|
+
const entryProducer = prdWorkflowRuntimeEventProducer(entry);
|
|
10869
|
+
const entryDedupeKey = prdWorkflowRuntimeEventDedupeKey(entry);
|
|
10762
10870
|
const index = current.events.findIndex((item) => {
|
|
10871
|
+
if (prdWorkflowRuntimeEventProducer(item) !== entryProducer) return false;
|
|
10763
10872
|
if (String(item?.id || "") === entry.id) return true;
|
|
10873
|
+
if (prdWorkflowRuntimeEventDedupeKey(item) === entryDedupeKey) return true;
|
|
10764
10874
|
if (!entryIdem) return false;
|
|
10765
10875
|
if (String(item?.idempotencyKey || "").trim() === entryIdem) return true;
|
|
10766
10876
|
return Array.isArray(item?.idempotencyHistory) && item.idempotencyHistory.includes(entryIdem);
|
|
@@ -10871,36 +10981,43 @@ function prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, event = {}) {
|
|
|
10871
10981
|
}
|
|
10872
10982
|
}
|
|
10873
10983
|
|
|
10874
|
-
function
|
|
10984
|
+
function prdWorkflowFindIdempotencyEvent(scopedRoot, tapdId, idempotencyKey, source = "", completedOnly = true) {
|
|
10875
10985
|
const key = String(idempotencyKey || "").trim();
|
|
10876
10986
|
if (!key) return null;
|
|
10987
|
+
const producer = String(source || "").trim().toLowerCase();
|
|
10877
10988
|
const events = prdWorkflowReadRuntimeEvents(scopedRoot, tapdId).events;
|
|
10878
10989
|
return [...events].reverse().find((event) => (
|
|
10990
|
+
(!producer || prdWorkflowRuntimeEventProducer(event) === producer) &&
|
|
10879
10991
|
(String(event?.idempotencyKey || "") === key || (Array.isArray(event?.idempotencyHistory) && event.idempotencyHistory.includes(key))) &&
|
|
10880
|
-
["done", "success", "completed"].includes(String(event?.status || "").toLowerCase())
|
|
10992
|
+
(!completedOnly || ["done", "success", "completed"].includes(String(event?.status || "").toLowerCase()))
|
|
10881
10993
|
)) || null;
|
|
10882
10994
|
}
|
|
10883
10995
|
|
|
10996
|
+
function prdWorkflowFindCompletedIdempotencyEvent(scopedRoot, tapdId, idempotencyKey, source = "") {
|
|
10997
|
+
return prdWorkflowFindIdempotencyEvent(scopedRoot, tapdId, idempotencyKey, source, true);
|
|
10998
|
+
}
|
|
10999
|
+
|
|
10884
11000
|
function prdWorkflowRuntimeEventDedupeKey(event = {}, index = 0) {
|
|
11001
|
+
const producer = prdWorkflowRuntimeEventProducer(event);
|
|
10885
11002
|
const stage = prdWorkflowRuntimeEventCanonicalStage(event);
|
|
10886
11003
|
const aggregateByStage = event.aggregateByStage !== false && event.aggregate_by_stage !== false;
|
|
10887
11004
|
if (stage) {
|
|
10888
11005
|
const issue = event?.issueKey || event?.issue_key || event?.issue;
|
|
10889
11006
|
const platform = event?.platform;
|
|
10890
11007
|
if (aggregateByStage || issue || platform) {
|
|
10891
|
-
return ["stage", event?.scope, issue, platform, stage]
|
|
11008
|
+
return ["producer", producer, "stage", event?.scope, issue, platform, stage]
|
|
10892
11009
|
.map((value) => String(value || "").trim())
|
|
10893
11010
|
.join(":");
|
|
10894
11011
|
}
|
|
10895
11012
|
}
|
|
10896
11013
|
const id = String(event?.id || event?.eventId || event?.event_id || "").trim();
|
|
10897
|
-
if (id) return `id:${id}`;
|
|
11014
|
+
if (id) return `producer:${producer}:id:${id}`;
|
|
10898
11015
|
if (stage) {
|
|
10899
|
-
return ["stage", event?.scope, event?.issueKey || event?.issue_key || event?.issue, event?.platform, stage]
|
|
11016
|
+
return ["producer", producer, "stage", event?.scope, event?.issueKey || event?.issue_key || event?.issue, event?.platform, stage]
|
|
10900
11017
|
.map((value) => String(value || "").trim())
|
|
10901
11018
|
.join(":");
|
|
10902
11019
|
}
|
|
10903
|
-
return `idx:${index}`;
|
|
11020
|
+
return `producer:${producer}:idx:${index}`;
|
|
10904
11021
|
}
|
|
10905
11022
|
|
|
10906
11023
|
function prdWorkflowMergeRuntimeEventList(snapshotEvents = [], runtimeEvents = []) {
|
|
@@ -11169,14 +11286,24 @@ function prdWorkflowMergeRuntimeEvents(scopedRoot, tapdId, snapshot) {
|
|
|
11169
11286
|
const globalState = prdWorkflowGlobalStateFromEvents(tapdId, snapshot, runtimeEvents);
|
|
11170
11287
|
const artifacts = mergeWorkflowArtifacts(snapshot?.artifacts, runtimeEvents);
|
|
11171
11288
|
const projections = materializeWorkflowProjections(snapshot, runtimeEvents);
|
|
11289
|
+
const extensions = materializeWorkflowExtensions(snapshot, runtimeEvents);
|
|
11290
|
+
const prdFlowExtension = extensions["prd-flow"] && typeof extensions["prd-flow"] === "object"
|
|
11291
|
+
? extensions["prd-flow"]
|
|
11292
|
+
: {};
|
|
11293
|
+
const prdFlowExtensionView = {};
|
|
11294
|
+
for (const key of ["issues", "issueGroups", "issue_groups", "epics", "epicGroups", "epic_groups", "aiDocs", "ai_docs"]) {
|
|
11295
|
+
if (Object.prototype.hasOwnProperty.call(prdFlowExtension, key)) prdFlowExtensionView[key] = prdFlowExtension[key];
|
|
11296
|
+
}
|
|
11172
11297
|
return {
|
|
11173
11298
|
...snapshot,
|
|
11299
|
+
...prdFlowExtensionView,
|
|
11174
11300
|
workflow: globalState.workflow,
|
|
11175
11301
|
overall,
|
|
11176
11302
|
globalState,
|
|
11177
11303
|
artifacts,
|
|
11178
11304
|
projections,
|
|
11179
|
-
|
|
11305
|
+
extensions,
|
|
11306
|
+
runtimeRevision: workflowRuntimeRevision(globalState, artifacts, runtimeEvents, projections, extensions),
|
|
11180
11307
|
runtimeEvents,
|
|
11181
11308
|
events,
|
|
11182
11309
|
sources: {
|
|
@@ -12955,6 +13082,8 @@ export function startUiServer({
|
|
|
12955
13082
|
}
|
|
12956
13083
|
|
|
12957
13084
|
if (req.method === "POST" && url.pathname === "/api/prd-workflow/snapshot") {
|
|
13085
|
+
res.setHeader("Deprecation", "true");
|
|
13086
|
+
res.setHeader("Link", "</api/workflows/report>; rel=\"successor-version\"");
|
|
12958
13087
|
if (!authUser?.userId) {
|
|
12959
13088
|
json(res, 401, { error: "Authentication required" });
|
|
12960
13089
|
return;
|
|
@@ -13188,6 +13317,10 @@ export function startUiServer({
|
|
|
13188
13317
|
json(res, 200, {
|
|
13189
13318
|
ok: true,
|
|
13190
13319
|
snapshot: withDiagnostic,
|
|
13320
|
+
compatibility: {
|
|
13321
|
+
deprecatedEndpoint: "/api/prd-workflow/snapshot",
|
|
13322
|
+
replacement: "/api/workflows/report with observation.state",
|
|
13323
|
+
},
|
|
13191
13324
|
...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
|
|
13192
13325
|
});
|
|
13193
13326
|
} catch (e) {
|
|
@@ -13751,6 +13884,24 @@ export function startUiServer({
|
|
|
13751
13884
|
String(currentSnapshot.runtimeRevision || "").trim(),
|
|
13752
13885
|
String(currentSnapshot.revision || "").trim(),
|
|
13753
13886
|
].filter(Boolean));
|
|
13887
|
+
if (report.idempotencyKey) {
|
|
13888
|
+
const existing = prdWorkflowFindCompletedIdempotencyEvent(
|
|
13889
|
+
scopedRoot,
|
|
13890
|
+
tapdId,
|
|
13891
|
+
report.idempotencyKey,
|
|
13892
|
+
report.event.source,
|
|
13893
|
+
);
|
|
13894
|
+
if (existing) {
|
|
13895
|
+
json(res, 200, {
|
|
13896
|
+
ok: true,
|
|
13897
|
+
alreadyApplied: true,
|
|
13898
|
+
report,
|
|
13899
|
+
event: existing,
|
|
13900
|
+
snapshot: currentSnapshot,
|
|
13901
|
+
});
|
|
13902
|
+
return;
|
|
13903
|
+
}
|
|
13904
|
+
}
|
|
13754
13905
|
if (report.expectedRevision && acceptedRevisions.size && !acceptedRevisions.has(report.expectedRevision)) {
|
|
13755
13906
|
json(res, 409, {
|
|
13756
13907
|
error: "Workflow state changed; refresh before reporting",
|
|
@@ -13764,28 +13915,36 @@ export function startUiServer({
|
|
|
13764
13915
|
});
|
|
13765
13916
|
return;
|
|
13766
13917
|
}
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13918
|
+
let observation = null;
|
|
13919
|
+
if (report.observation) {
|
|
13920
|
+
const observationPayload = {
|
|
13921
|
+
...payload,
|
|
13922
|
+
tapdId,
|
|
13923
|
+
clientId: report.observation.clientId || payload.clientId || payload.source || "workflow-reporter",
|
|
13924
|
+
observedAt: report.observation.observedAt || payload.observedAt || "",
|
|
13925
|
+
scope: report.observation.scope || payload.scope || "client",
|
|
13926
|
+
};
|
|
13927
|
+
observation = prdWorkflowStoreClientObservation({
|
|
13928
|
+
scopedRoot,
|
|
13929
|
+
tapdId,
|
|
13930
|
+
rawState: report.observation.state,
|
|
13931
|
+
payload: observationPayload,
|
|
13932
|
+
req,
|
|
13933
|
+
userCtx,
|
|
13934
|
+
flowSource,
|
|
13935
|
+
flowId,
|
|
13936
|
+
});
|
|
13779
13937
|
}
|
|
13780
|
-
const
|
|
13938
|
+
const shouldStoreEvent = report.hasRuntimeUpdate || Boolean(report.idempotencyKey);
|
|
13939
|
+
const event = shouldStoreEvent ? prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, {
|
|
13781
13940
|
...report.event,
|
|
13782
13941
|
tapdId,
|
|
13783
13942
|
actor: {
|
|
13784
13943
|
userId: String(userCtx.userId || ""),
|
|
13785
13944
|
username: String(authUser.username || userCtx.userId || ""),
|
|
13786
13945
|
},
|
|
13787
|
-
});
|
|
13788
|
-
if (!event) throw new Error("Failed to store workflow report");
|
|
13946
|
+
}) : null;
|
|
13947
|
+
if (shouldStoreEvent && !event) throw new Error("Failed to store workflow report");
|
|
13789
13948
|
const snapshot = prdWorkflowWithAgentflowTokenDiagnostic(
|
|
13790
13949
|
prdWorkflowMaterializeSnapshot(
|
|
13791
13950
|
workflowScope.executionRoot,
|
|
@@ -13798,9 +13957,20 @@ export function startUiServer({
|
|
|
13798
13957
|
);
|
|
13799
13958
|
prdWorkflowBroadcast(
|
|
13800
13959
|
prdWorkflowKey(userCtx, flowSource, flowId, tapdId),
|
|
13801
|
-
{ type: "workflow-report", tapdId, workflow: report.workflow, event, snapshot },
|
|
13960
|
+
{ type: "workflow-report", tapdId, workflow: report.workflow, event, observation: Boolean(observation), snapshot },
|
|
13802
13961
|
);
|
|
13803
|
-
json(res, 200, {
|
|
13962
|
+
json(res, 200, {
|
|
13963
|
+
ok: true,
|
|
13964
|
+
report,
|
|
13965
|
+
event,
|
|
13966
|
+
observation: observation ? {
|
|
13967
|
+
accepted: true,
|
|
13968
|
+
clientId: observation.reportMeta.clientId,
|
|
13969
|
+
observedAt: observation.reportMeta.observedAt,
|
|
13970
|
+
schema: report.observation.schema,
|
|
13971
|
+
} : null,
|
|
13972
|
+
snapshot,
|
|
13973
|
+
});
|
|
13804
13974
|
} catch (e) {
|
|
13805
13975
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
13806
13976
|
}
|
|
@@ -13808,6 +13978,8 @@ export function startUiServer({
|
|
|
13808
13978
|
}
|
|
13809
13979
|
|
|
13810
13980
|
if (req.method === "POST" && url.pathname === "/api/prd-workflow/event") {
|
|
13981
|
+
res.setHeader("Deprecation", "true");
|
|
13982
|
+
res.setHeader("Link", "</api/workflows/report>; rel=\"successor-version\"");
|
|
13811
13983
|
if (!authUser?.userId) {
|
|
13812
13984
|
json(res, 401, { error: "Authentication required" });
|
|
13813
13985
|
return;
|
|
@@ -13858,14 +14030,26 @@ export function startUiServer({
|
|
|
13858
14030
|
getSessionTokenFromRequest(req) || "",
|
|
13859
14031
|
);
|
|
13860
14032
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "runtime-event", tapdId, event, snapshot });
|
|
13861
|
-
json(res, 200, {
|
|
14033
|
+
json(res, 200, {
|
|
14034
|
+
ok: true,
|
|
14035
|
+
event,
|
|
14036
|
+
snapshot,
|
|
14037
|
+
compatibility: {
|
|
14038
|
+
deprecatedEndpoint: "/api/prd-workflow/event",
|
|
14039
|
+
replacement: "/api/workflows/report with action/artifacts/extensions",
|
|
14040
|
+
},
|
|
14041
|
+
});
|
|
13862
14042
|
} catch (e) {
|
|
13863
14043
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
13864
14044
|
}
|
|
13865
14045
|
return;
|
|
13866
14046
|
}
|
|
13867
14047
|
|
|
13868
|
-
if (req.method === "POST" &&
|
|
14048
|
+
if (req.method === "POST" && (
|
|
14049
|
+
url.pathname === "/api/workflow-artifacts/publish" ||
|
|
14050
|
+
url.pathname === "/api/prd-workflow/review-link"
|
|
14051
|
+
)) {
|
|
14052
|
+
const legacyReviewEndpoint = url.pathname === "/api/prd-workflow/review-link";
|
|
13869
14053
|
if (!authUser?.userId) {
|
|
13870
14054
|
json(res, 401, { error: "Authentication required" });
|
|
13871
14055
|
return;
|
|
@@ -13878,11 +14062,16 @@ export function startUiServer({
|
|
|
13878
14062
|
return;
|
|
13879
14063
|
}
|
|
13880
14064
|
try {
|
|
13881
|
-
const
|
|
13882
|
-
if (
|
|
13883
|
-
json(res, 400, { error:
|
|
14065
|
+
const workflow = normalizeWorkflowReference(payload);
|
|
14066
|
+
if (workflow.error) {
|
|
14067
|
+
json(res, 400, { error: workflow.error });
|
|
14068
|
+
return;
|
|
14069
|
+
}
|
|
14070
|
+
if (workflow.namespace !== "tapd") {
|
|
14071
|
+
json(res, 400, { error: `Unsupported workflow namespace: ${workflow.namespace}` });
|
|
13884
14072
|
return;
|
|
13885
14073
|
}
|
|
14074
|
+
const tapdId = workflow.id;
|
|
13886
14075
|
const flowId = String(payload.flowId || "").trim();
|
|
13887
14076
|
const flowSource = String(payload.flowSource || "user").trim() || "user";
|
|
13888
14077
|
const archived = payload.archived === true || payload.flowArchived === true;
|
|
@@ -13899,6 +14088,68 @@ export function startUiServer({
|
|
|
13899
14088
|
}
|
|
13900
14089
|
const scopedRoot = workflowScope.stateRoot;
|
|
13901
14090
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
14091
|
+
const producer = String(payload.source || "agentflow-cli").trim().toLowerCase() || "agentflow-cli";
|
|
14092
|
+
if (!/^[a-z][a-z0-9._-]{0,119}$/.test(producer)) {
|
|
14093
|
+
json(res, 400, { error: "Invalid workflow report source" });
|
|
14094
|
+
return;
|
|
14095
|
+
}
|
|
14096
|
+
const idempotencyKey = String(
|
|
14097
|
+
payload.idempotencyKey || payload.idempotency_key || "",
|
|
14098
|
+
).trim();
|
|
14099
|
+
const currentSnapshot = prdWorkflowMaterializeSnapshot(
|
|
14100
|
+
workflowScope.executionRoot,
|
|
14101
|
+
scopedRoot,
|
|
14102
|
+
tapdId,
|
|
14103
|
+
userCtx,
|
|
14104
|
+
{ flowSource, flowId },
|
|
14105
|
+
);
|
|
14106
|
+
const expectedRevision = String(payload.expectedRevision || payload.expected_revision || "").trim();
|
|
14107
|
+
const acceptedRevisions = new Set([
|
|
14108
|
+
String(currentSnapshot.runtimeRevision || "").trim(),
|
|
14109
|
+
String(currentSnapshot.revision || "").trim(),
|
|
14110
|
+
].filter(Boolean));
|
|
14111
|
+
if (idempotencyKey) {
|
|
14112
|
+
const existing = prdWorkflowFindIdempotencyEvent(
|
|
14113
|
+
scopedRoot,
|
|
14114
|
+
tapdId,
|
|
14115
|
+
idempotencyKey,
|
|
14116
|
+
producer,
|
|
14117
|
+
false,
|
|
14118
|
+
);
|
|
14119
|
+
if (existing) {
|
|
14120
|
+
const artifact = Array.isArray(existing.artifacts) ? existing.artifacts[0] : null;
|
|
14121
|
+
json(res, 200, {
|
|
14122
|
+
ok: true,
|
|
14123
|
+
alreadyApplied: true,
|
|
14124
|
+
workflow,
|
|
14125
|
+
artifact,
|
|
14126
|
+
review: artifact ? {
|
|
14127
|
+
id: existing.reviewId || "",
|
|
14128
|
+
url: artifact.canonicalUrl || artifact.url || "",
|
|
14129
|
+
shortUrl: artifact.shortUrl || "",
|
|
14130
|
+
shortCode: existing.reviewShortCode || "",
|
|
14131
|
+
durability: existing.durability || artifact.durability || "",
|
|
14132
|
+
expiresAt: existing.expiresAt || artifact.expiresAt || "",
|
|
14133
|
+
} : null,
|
|
14134
|
+
event: existing,
|
|
14135
|
+
snapshot: currentSnapshot,
|
|
14136
|
+
});
|
|
14137
|
+
return;
|
|
14138
|
+
}
|
|
14139
|
+
}
|
|
14140
|
+
if (expectedRevision && acceptedRevisions.size && !acceptedRevisions.has(expectedRevision)) {
|
|
14141
|
+
json(res, 409, {
|
|
14142
|
+
error: "Workflow state changed; refresh before publishing",
|
|
14143
|
+
conflict: {
|
|
14144
|
+
type: "workflow-revision-conflict",
|
|
14145
|
+
expectedRevision,
|
|
14146
|
+
currentRevision: currentSnapshot.runtimeRevision || currentSnapshot.revision || "",
|
|
14147
|
+
workflow,
|
|
14148
|
+
},
|
|
14149
|
+
snapshot: currentSnapshot,
|
|
14150
|
+
});
|
|
14151
|
+
return;
|
|
14152
|
+
}
|
|
13902
14153
|
const review = prdWorkflowCreateReview(
|
|
13903
14154
|
scopedRoot,
|
|
13904
14155
|
tapdId,
|
|
@@ -13932,9 +14183,6 @@ export function startUiServer({
|
|
|
13932
14183
|
const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
|
|
13933
14184
|
? review.source
|
|
13934
14185
|
: { kind: durability === "durable" ? "ai-doc" : "local-draft", durability };
|
|
13935
|
-
const idempotencyKey = String(
|
|
13936
|
-
payload.idempotencyKey || payload.idempotency_key || "",
|
|
13937
|
-
).trim();
|
|
13938
14186
|
const artifact = {
|
|
13939
14187
|
key: artifactKey,
|
|
13940
14188
|
label: payload.artifactLabel || "Markdown Review",
|
|
@@ -13950,6 +14198,7 @@ export function startUiServer({
|
|
|
13950
14198
|
issueKey: payload.issueKey || payload.issue_key || "",
|
|
13951
14199
|
platform: payload.platform || "",
|
|
13952
14200
|
stageKey: reviewStageKey,
|
|
14201
|
+
producer,
|
|
13953
14202
|
...(reviewMrUrl ? { mrUrl: reviewMrUrl } : {}),
|
|
13954
14203
|
...(reviewMrIid ? { mrIid: reviewMrIid } : {}),
|
|
13955
14204
|
...(reviewCommitSha ? { commitSha: reviewCommitSha } : {}),
|
|
@@ -13957,6 +14206,7 @@ export function startUiServer({
|
|
|
13957
14206
|
const event = prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, {
|
|
13958
14207
|
id: `review-link:${artifactKey}`,
|
|
13959
14208
|
type: "review-link",
|
|
14209
|
+
source: producer,
|
|
13960
14210
|
auxiliary: true,
|
|
13961
14211
|
aggregateByStage: false,
|
|
13962
14212
|
conflictOnArtifact: false,
|
|
@@ -13988,6 +14238,7 @@ export function startUiServer({
|
|
|
13988
14238
|
persistence: "runtime",
|
|
13989
14239
|
durability,
|
|
13990
14240
|
source: reviewSource,
|
|
14241
|
+
producer,
|
|
13991
14242
|
expiresAt: review.expiresAt || "",
|
|
13992
14243
|
issueKey: artifact.issueKey,
|
|
13993
14244
|
platform: artifact.platform,
|
|
@@ -14004,8 +14255,14 @@ export function startUiServer({
|
|
|
14004
14255
|
getSessionTokenFromRequest(req) || "",
|
|
14005
14256
|
);
|
|
14006
14257
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "review-link", tapdId, event, snapshot });
|
|
14258
|
+
if (legacyReviewEndpoint) {
|
|
14259
|
+
res.setHeader("Deprecation", "true");
|
|
14260
|
+
res.setHeader("Link", "</api/workflow-artifacts/publish>; rel=\"successor-version\"");
|
|
14261
|
+
}
|
|
14007
14262
|
json(res, 200, {
|
|
14008
14263
|
ok: true,
|
|
14264
|
+
workflow,
|
|
14265
|
+
artifact,
|
|
14009
14266
|
review: {
|
|
14010
14267
|
...review,
|
|
14011
14268
|
url: reviewUrl,
|
|
@@ -14014,6 +14271,12 @@ export function startUiServer({
|
|
|
14014
14271
|
},
|
|
14015
14272
|
event,
|
|
14016
14273
|
snapshot,
|
|
14274
|
+
...(legacyReviewEndpoint ? {
|
|
14275
|
+
compatibility: {
|
|
14276
|
+
deprecatedEndpoint: "/api/prd-workflow/review-link",
|
|
14277
|
+
replacement: "/api/workflow-artifacts/publish",
|
|
14278
|
+
},
|
|
14279
|
+
} : {}),
|
|
14017
14280
|
});
|
|
14018
14281
|
} catch (e) {
|
|
14019
14282
|
json(res, 500, { error: (e && e.message) || String(e) });
|