@abelxiaoxing/cadence 1.8.2 → 1.8.4

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.
@@ -26,7 +26,6 @@ import type {
26
26
  TaskLedger,
27
27
  VerifiedTaskEvent,
28
28
  } from "./task-ledger.ts";
29
- import type { RunWorkerBroker } from "./worker-broker.ts";
30
29
  import {
31
30
  type DependencyContractEntry,
32
31
  DependencyManifestError,
@@ -39,11 +38,9 @@ import {
39
38
  hash,
40
39
  hasUnapprovedDependencyChange,
41
40
  hasVerificationLifecycle,
42
- implementationRouteRequirements,
43
41
  isRecord,
44
42
  LOCKFILES,
45
43
  SHA256,
46
- semanticRouteFailure,
47
44
  taskExecutionContract,
48
45
  type WorkflowAttemptOutcome,
49
46
  type WorkflowRouteFacts,
@@ -81,17 +78,14 @@ export class PhaseExecution {
81
78
  DurableWorkflowEngineOptions,
82
79
  "verificationPolicy" | "verificationEnvironment" | "proposeCandidate"
83
80
  >;
84
- readonly #broker: RunWorkerBroker;
85
81
  constructor(
86
82
  options: Pick<
87
83
  DurableWorkflowEngineOptions,
88
84
  "verificationPolicy" | "verificationEnvironment" | "proposeCandidate"
89
85
  >,
90
- broker: RunWorkerBroker,
91
86
  services: PhaseExecutionServices,
92
87
  ) {
93
88
  this.#options = options;
94
- this.#broker = broker;
95
89
  this.#services = services;
96
90
  }
97
91
  #recordCorrection(
@@ -559,25 +553,6 @@ export class PhaseExecution {
559
553
  proposalRoot,
560
554
  request.signal,
561
555
  );
562
- if (request.routeId && !pending) {
563
- const rebound = this.#broker.resumeBinding({
564
- runId: request.runId,
565
- taskId: request.taskId,
566
- role: "implementation-worker",
567
- routeId: request.routeId,
568
- ...(request.routeFingerprint
569
- ? { expectedFingerprint: request.routeFingerprint }
570
- : {}),
571
- requirements: implementationRouteRequirements({
572
- task: request.task,
573
- ...(request.artifactCorrection
574
- ? { artifactCorrection: request.artifactCorrection }
575
- : {}),
576
- repair: { failureIdentities: input.failureIdentities },
577
- }),
578
- });
579
- if (!rebound.ok) return { kind: "paused", code: rebound.code };
580
- }
581
556
  const ledgerProjection = ledger.projection({
582
557
  runId: request.runId,
583
558
  taskId: request.taskId,
@@ -588,99 +563,76 @@ export class PhaseExecution {
588
563
  attribution: "introduced" as const,
589
564
  failureIdentities: [...input.failureIdentities],
590
565
  };
591
- let identity: BeginCandidateInput | undefined = pending?.identity;
592
- const routed = pending
593
- ? {
594
- ok: true as const,
595
- value: pending.proposal,
596
- routeId: pending.identity.routeId,
597
- routeFingerprint: pending.identity.routeFingerprint,
598
- }
599
- : await this.#broker.run({
600
- runId: request.runId,
601
- taskId: request.taskId,
602
- operationId: `${request.operationId}:${request.taskId}:repair:${input.repairAttempt}`,
603
- role: "implementation-worker",
604
- requirements: implementationRouteRequirements({
605
- task: request.task,
606
- ...(request.artifactCorrection
607
- ? { artifactCorrection: request.artifactCorrection }
608
- : {}),
609
- repair: { failureIdentities: input.failureIdentities },
610
- }),
611
- signal: request.signal,
612
- ...(request.onActivity ? { onActivity: request.onActivity } : {}),
613
- classifyResult: semanticRouteFailure,
614
- execute: async (attempt) => {
615
- identity = {
616
- candidateId: `candidate-${randomUUID()}`,
617
- runId: request.runId,
618
- deliveryRevision: request.deliveryRevision,
619
- taskId: request.taskId,
620
- phase: request.phase,
621
- attemptId: `repair-${hash(
622
- request.operationId,
623
- request.taskId,
624
- String(input.repairAttempt),
625
- attempt.route.fingerprint,
626
- ).slice(0, 40)}`,
627
- approvedPaths,
628
- isolatedRevisionId: baseRevisionId,
629
- verificationId: request.task.repairVerification.id,
630
- routeId: attempt.route.id,
631
- routeFingerprint: attempt.route.fingerprint,
632
- };
633
- if (request.reserveCandidate && !request.reserveCandidate())
634
- return {
635
- kind: "paused" as const,
636
- code:
637
- request.additionalAttempt || request.verificationOnly
638
- ? "repair-attempts-exhausted"
639
- : "change-work-budget-exhausted",
640
- };
641
- return this.#options.proposeCandidate({
642
- runId: request.runId,
643
- operationId: request.operationId,
644
- deliveryRevision: request.deliveryRevision,
645
- taskId: request.taskId,
646
- phase: request.phase,
647
- task: structuredClone(request.task),
648
- contextReadPaths: request.contextReadPaths,
649
- workspaceRoot: proposalRoot,
650
- ledgerProjection,
651
- candidateArtifact: {
652
- ledger,
653
- identity,
654
- workspaceRoot: proposalRoot,
655
- writePaths: approvedWritePaths,
656
- deletePaths: approvedDeletePaths,
657
- },
658
- route: attempt.route,
659
- repair,
660
- ...(request.artifactCorrection
661
- ? { artifactCorrection: request.artifactCorrection }
662
- : {}),
663
- ...(request.contextRequest
664
- ? { contextRequest: request.contextRequest }
665
- : {}),
666
- ...(request.recoveryFeedback
667
- ? { recoveryFeedback: request.recoveryFeedback }
668
- : {}),
669
- signal: attempt.signal,
670
- onRequestStart: attempt.onRequestStart,
671
- onHeaders: attempt.onHeaders,
672
- onProgress: attempt.onProgress,
673
- });
566
+ const launch = async () => {
567
+ const identity: BeginCandidateInput = {
568
+ candidateId: `candidate-${randomUUID()}`,
569
+ runId: request.runId,
570
+ deliveryRevision: request.deliveryRevision,
571
+ taskId: request.taskId,
572
+ phase: request.phase,
573
+ attemptId: `repair-${hash(
574
+ request.operationId,
575
+ request.taskId,
576
+ String(input.repairAttempt),
577
+ hash("pi-process"),
578
+ ).slice(0, 40)}`,
579
+ approvedPaths,
580
+ isolatedRevisionId: baseRevisionId,
581
+ verificationId: request.task.repairVerification.id,
582
+ routeId: "pi-process",
583
+ routeFingerprint: hash("pi-process"),
584
+ };
585
+ if (request.reserveCandidate && !request.reserveCandidate())
586
+ return {
587
+ identity,
588
+ proposal: {
589
+ kind: "paused" as const,
590
+ code:
591
+ request.additionalAttempt || request.verificationOnly
592
+ ? "repair-attempts-exhausted"
593
+ : "change-work-budget-exhausted",
674
594
  },
675
- });
676
- if (!routed.ok) {
677
- return routed.state === "cancelled" || request.signal.aborted
678
- ? { kind: "operation-cancelled", code: "cancelled" }
679
- : { kind: "paused", code: routed.code };
680
- }
681
- if (!identity || identity.routeId !== routed.routeId) {
682
- throw new Error("candidate-route-identity-invalid");
683
- }
595
+ };
596
+ const proposal = await this.#options.proposeCandidate({
597
+ runId: request.runId,
598
+ operationId: request.operationId,
599
+ deliveryRevision: request.deliveryRevision,
600
+ taskId: request.taskId,
601
+ phase: request.phase,
602
+ task: structuredClone(request.task),
603
+ contextReadPaths: request.contextReadPaths,
604
+ workspaceRoot: proposalRoot,
605
+ ledgerProjection,
606
+ candidateArtifact: {
607
+ ledger,
608
+ identity,
609
+ workspaceRoot: proposalRoot,
610
+ writePaths: approvedWritePaths,
611
+ deletePaths: approvedDeletePaths,
612
+ },
613
+ repair,
614
+ ...(request.artifactCorrection
615
+ ? { artifactCorrection: request.artifactCorrection }
616
+ : {}),
617
+ ...(request.contextRequest
618
+ ? { contextRequest: request.contextRequest }
619
+ : {}),
620
+ ...(request.recoveryFeedback
621
+ ? { recoveryFeedback: request.recoveryFeedback }
622
+ : {}),
623
+ signal: request.signal,
624
+ onRequestStart: () =>
625
+ emitWorkflowActivity(request.onActivity, {
626
+ state: "waiting-first-response",
627
+ }),
628
+ onHeaders: () =>
629
+ emitWorkflowActivity(request.onActivity, { state: "running" }),
630
+ onProgress: () =>
631
+ emitWorkflowActivity(request.onActivity, { state: "running" }),
632
+ });
633
+ return { identity, proposal };
634
+ };
635
+ const { identity, proposal } = pending ?? (await launch());
684
636
  const correctionIdentity = identity;
685
637
  const withRoute = <T extends object>(outcome: T) => ({
686
638
  ...outcome,
@@ -708,7 +660,6 @@ export class PhaseExecution {
708
660
  this.#recordCorrection(ledger, request, correctionIdentity, correction);
709
661
  return withRoute({ ...outcome, retryPolicy: correction.category });
710
662
  };
711
- const proposal = routed.value;
712
663
  if (
713
664
  proposal.kind !== "candidate" &&
714
665
  proposal.kind !== "sealed-candidate"
@@ -1651,123 +1602,77 @@ export class PhaseExecution {
1651
1602
  proposalRoot,
1652
1603
  input.signal,
1653
1604
  );
1654
- if (input.routeId && !pending) {
1655
- const rebound = this.#broker.resumeBinding({
1656
- runId: input.runId,
1657
- taskId: input.taskId,
1658
- role: "implementation-worker",
1659
- routeId: input.routeId,
1660
- ...(input.routeFingerprint
1661
- ? { expectedFingerprint: input.routeFingerprint }
1662
- : {}),
1663
- requirements: implementationRouteRequirements({
1664
- task: input.task,
1665
- ...(input.artifactCorrection
1666
- ? { artifactCorrection: input.artifactCorrection }
1667
- : {}),
1668
- ...(input.repair ? { repair: input.repair } : {}),
1669
- }),
1670
- });
1671
- if (!rebound.ok) {
1672
- return retained({ kind: "paused", code: rebound.code });
1673
- }
1674
- }
1675
1605
  const ledgerProjection = ledger.projection({
1676
1606
  runId: input.runId,
1677
1607
  taskId: input.taskId,
1678
1608
  nextPhase: input.phase,
1679
1609
  });
1680
- let identity: BeginCandidateInput | undefined = pending?.identity;
1681
- const routed = pending
1682
- ? {
1683
- ok: true as const,
1684
- value: pending.proposal,
1685
- routeId: pending.identity.routeId,
1686
- routeFingerprint: pending.identity.routeFingerprint,
1687
- }
1688
- : await this.#broker.run({
1689
- runId: input.runId,
1690
- taskId: input.taskId,
1691
- operationId: `${input.operationId}:${input.taskId}:${input.phase}`,
1692
- role: "implementation-worker",
1693
- requirements: implementationRouteRequirements({
1694
- task: input.task,
1695
- ...(input.artifactCorrection
1696
- ? { artifactCorrection: input.artifactCorrection }
1697
- : {}),
1698
- ...(input.repair ? { repair: input.repair } : {}),
1699
- }),
1700
- signal: input.signal,
1701
- ...(input.onActivity ? { onActivity: input.onActivity } : {}),
1702
- classifyResult: semanticRouteFailure,
1703
- execute: async (attempt) => {
1704
- identity = {
1705
- candidateId: `candidate-${randomUUID()}`,
1706
- runId: input.runId,
1707
- deliveryRevision: input.deliveryRevision,
1708
- taskId: input.taskId,
1709
- phase: input.phase,
1710
- attemptId: `attempt-${hash(
1711
- input.operationId,
1712
- input.taskId,
1713
- input.phase,
1714
- attempt.route.fingerprint,
1715
- ).slice(0, 40)}`,
1716
- approvedPaths,
1717
- isolatedRevisionId: baseRevisionId,
1718
- verificationId: phase.verification.id,
1719
- routeId: attempt.route.id,
1720
- routeFingerprint: attempt.route.fingerprint,
1721
- };
1722
- if (input.reserveCandidate && !input.reserveCandidate())
1723
- return {
1724
- kind: "paused" as const,
1725
- code:
1726
- input.additionalAttempt || input.verificationOnly
1727
- ? "repair-attempts-exhausted"
1728
- : "change-work-budget-exhausted",
1729
- };
1730
- return this.#options.proposeCandidate({
1731
- runId: input.runId,
1732
- operationId: input.operationId,
1733
- deliveryRevision: input.deliveryRevision,
1734
- taskId: input.taskId,
1735
- phase: input.phase,
1736
- task: structuredClone(input.task),
1737
- contextReadPaths: input.contextReadPaths,
1738
- workspaceRoot: proposalRoot,
1739
- ledgerProjection,
1740
- candidateArtifact: {
1741
- ledger,
1742
- identity,
1743
- workspaceRoot: proposalRoot,
1744
- writePaths: phase.write,
1745
- deletePaths: phase.delete,
1746
- },
1747
- route: attempt.route,
1748
- ...(input.contextRequest
1749
- ? { contextRequest: input.contextRequest }
1750
- : {}),
1751
- ...(input.recoveryFeedback
1752
- ? { recoveryFeedback: input.recoveryFeedback }
1753
- : {}),
1754
- signal: attempt.signal,
1755
- onRequestStart: attempt.onRequestStart,
1756
- onHeaders: attempt.onHeaders,
1757
- onProgress: attempt.onProgress,
1758
- });
1610
+ const launch = async () => {
1611
+ const identity: BeginCandidateInput = {
1612
+ candidateId: `candidate-${randomUUID()}`,
1613
+ runId: input.runId,
1614
+ deliveryRevision: input.deliveryRevision,
1615
+ taskId: input.taskId,
1616
+ phase: input.phase,
1617
+ attemptId: `attempt-${hash(
1618
+ input.operationId,
1619
+ input.taskId,
1620
+ input.phase,
1621
+ hash("pi-process"),
1622
+ ).slice(0, 40)}`,
1623
+ approvedPaths,
1624
+ isolatedRevisionId: baseRevisionId,
1625
+ verificationId: phase.verification.id,
1626
+ routeId: "pi-process",
1627
+ routeFingerprint: hash("pi-process"),
1628
+ };
1629
+ if (input.reserveCandidate && !input.reserveCandidate())
1630
+ return {
1631
+ identity,
1632
+ proposal: {
1633
+ kind: "paused" as const,
1634
+ code:
1635
+ input.additionalAttempt || input.verificationOnly
1636
+ ? "repair-attempts-exhausted"
1637
+ : "change-work-budget-exhausted",
1759
1638
  },
1760
- });
1761
- if (!routed.ok) {
1762
- return retained(
1763
- routed.state === "cancelled" || input.signal.aborted
1764
- ? { kind: "operation-cancelled", code: "cancelled" }
1765
- : { kind: "paused", code: routed.code },
1766
- );
1767
- }
1768
- if (!identity || identity.routeId !== routed.routeId) {
1769
- throw new Error("candidate-route-identity-invalid");
1770
- }
1639
+ };
1640
+ const proposal = await this.#options.proposeCandidate({
1641
+ runId: input.runId,
1642
+ operationId: input.operationId,
1643
+ deliveryRevision: input.deliveryRevision,
1644
+ taskId: input.taskId,
1645
+ phase: input.phase,
1646
+ task: structuredClone(input.task),
1647
+ contextReadPaths: input.contextReadPaths,
1648
+ workspaceRoot: proposalRoot,
1649
+ ledgerProjection,
1650
+ candidateArtifact: {
1651
+ ledger,
1652
+ identity,
1653
+ workspaceRoot: proposalRoot,
1654
+ writePaths: phase.write,
1655
+ deletePaths: phase.delete,
1656
+ },
1657
+ ...(input.contextRequest
1658
+ ? { contextRequest: input.contextRequest }
1659
+ : {}),
1660
+ ...(input.recoveryFeedback
1661
+ ? { recoveryFeedback: input.recoveryFeedback }
1662
+ : {}),
1663
+ signal: input.signal,
1664
+ onRequestStart: () =>
1665
+ emitWorkflowActivity(input.onActivity, {
1666
+ state: "waiting-first-response",
1667
+ }),
1668
+ onHeaders: () =>
1669
+ emitWorkflowActivity(input.onActivity, { state: "running" }),
1670
+ onProgress: () =>
1671
+ emitWorkflowActivity(input.onActivity, { state: "running" }),
1672
+ });
1673
+ return { identity, proposal };
1674
+ };
1675
+ const { identity, proposal } = pending ?? (await launch());
1771
1676
  const correctionIdentity = identity;
1772
1677
  selectedRoute = {
1773
1678
  routeId: identity.routeId,
@@ -1794,7 +1699,6 @@ export class PhaseExecution {
1794
1699
  this.#recordCorrection(ledger, input, correctionIdentity, correction);
1795
1700
  return retained({ ...outcome, retryPolicy: correction.category });
1796
1701
  };
1797
- const proposal = routed.value;
1798
1702
  if (
1799
1703
  proposal.kind !== "candidate" &&
1800
1704
  proposal.kind !== "sealed-candidate"
@@ -26,22 +26,19 @@ export function startupCardLines(options: {
26
26
  home: string;
27
27
  projectTrusted: boolean;
28
28
  }): string[] {
29
- const lines = ["Cadence · 研究能力(仅本地配置检查,未联网验证)"];
29
+ const lines: string[] = [];
30
30
  if (!options.projectTrusted) {
31
- return [
32
- ...lines,
33
- "尚未信任当前项目:未读取研究配置;信任项目后重新加载可查看状态。",
34
- "不自动启动 Abel 工作流;请勿在聊天中发送 API Key。",
35
- ];
31
+ return ["Cadence:当前项目未信任,未读取研究配置。"];
36
32
  }
37
33
 
38
34
  let selectedPath: string | null = null;
39
35
  try {
40
36
  selectedPath = selectConfigPath(options);
41
37
  const { values } = loadConfig({ ...options, allowMissing: true });
42
- const context7 = !validUrl(
38
+ const context7UrlValid = validUrl(
43
39
  values.CONTEXT7_API_URL || "https://context7.com/api/v2",
44
- )
40
+ );
41
+ const context7 = !context7UrlValid
45
42
  ? "CONTEXT7_API_URL 格式无效"
46
43
  : values.CONTEXT7_API_KEY
47
44
  ? "已配置密钥"
@@ -49,36 +46,33 @@ export function startupCardLines(options: {
49
46
  const missing = ["GROK_API_URL", "GROK_API_KEY"].filter(
50
47
  (name) => !values[name],
51
48
  );
49
+ const grokUrlValid = !missing.length && validUrl(values.GROK_API_URL);
52
50
  const grok = missing.length
53
51
  ? `待配置 ${missing.join("、")}`
54
- : !validUrl(values.GROK_API_URL)
52
+ : !grokUrlValid
55
53
  ? "GROK_API_URL 格式无效"
56
54
  : "已配置";
55
+ const tavilyUrlValid = validUrl(
56
+ values.TAVILY_API_URL || "https://api.tavily.com",
57
+ );
57
58
  const tavily =
58
59
  values.TAVILY_ENABLED === "false"
59
60
  ? "已关闭"
60
61
  : !values.TAVILY_API_KEY
61
62
  ? "未配置(可选)"
62
- : !validUrl(values.TAVILY_API_URL || "https://api.tavily.com")
63
+ : !tavilyUrlValid
63
64
  ? "TAVILY_API_URL 格式无效"
64
- : "已配置(Grok Skill 仍需 Grok 配置)";
65
- lines.push(
66
- `文档查询 Context7:${context7}`,
67
- `联网搜索 Grok:${grok};Tavily:${tavily}`,
68
- );
65
+ : "已配置";
66
+ lines.push(`Context7:${context7}|Grok:${grok}|Tavily:${tavily}`);
69
67
  } catch {
70
- lines.push("研究配置无法读取或解析:请检查下方文件;未回退到其他配置。");
68
+ lines.push("研究配置无法读取或解析:请检查配置文件;未回退到其他配置。");
71
69
  }
72
70
 
73
71
  const userPath = path.join(options.home, ".pi", "agent", "cadence", ".env");
74
- const projectPath = path.join(options.cwd, ".pi", "cadence", ".env");
75
- lines.push(
72
+ lines.unshift(
76
73
  selectedPath
77
- ? `生效文件:${displayPath(selectedPath)}${selectedPath === projectPath ? "(项目整文件优先,不合并用户配置)" : "(用户级,跨项目复用)"}`
78
- : `配置位置:${displayPath(userPath)}(用户级,跨项目复用)`,
79
- "在本地编辑该文件:GROK_API_URL=<服务地址>、GROK_API_KEY=<密钥>,每项一行;可选 TAVILY_API_KEY。",
80
- "研究 Skills 不读取 shell API 变量;保存后下次调用生效。不要把密钥发到聊天或提交到 Git。",
81
- "可选服务未配置不阻塞普通任务,也不自动启动 Abel 工作流。",
74
+ ? `Cadence配置文件:${displayPath(selectedPath)}`
75
+ : `Cadence配置文件:${displayPath(userPath)}(未创建)`,
82
76
  );
83
77
  return lines;
84
78
  }