@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.
- package/README.md +71 -531
- package/agents/design-explorer.md +15 -23
- package/agents/diagnosis-worker.md +12 -23
- package/agents/implementation-worker.md +16 -34
- package/config/.env.example +2 -5
- package/package.json +1 -1
- package/prompts/abel-diagnose.md +5 -5
- package/src/candidate-context.ts +289 -0
- package/src/contracts.ts +12 -22
- package/src/durable-contracts.ts +1 -5
- package/src/durable-workflow.ts +3 -159
- package/src/evidence-draft.ts +1 -1
- package/src/index.ts +12 -16
- package/src/package-candidate-process.ts +206 -0
- package/src/package-candidate.ts +11 -214
- package/src/package-workflow.ts +5 -42
- package/src/packet-runtime.ts +144 -255
- package/src/phase-execution.ts +134 -230
- package/src/startup-card.ts +17 -23
- package/src/subagent-process.ts +476 -0
- package/src/transport-budget.ts +1 -3
- package/src/workflow-policy.ts +9 -18
- package/src/workflow-state-machine.ts +1 -15
- package/config/routes.example.json +0 -39
- package/src/child-budget.ts +0 -58
- package/src/child-model.ts +0 -142
- package/src/child-session.ts +0 -640
- package/src/parent-provider.ts +0 -375
- package/src/route-policy.ts +0 -545
- package/src/submit-tool.ts +0 -950
- package/src/worker-broker.ts +0 -806
package/src/phase-execution.ts
CHANGED
|
@@ -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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
:
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
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
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
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"
|
package/src/startup-card.ts
CHANGED
|
@@ -26,22 +26,19 @@ export function startupCardLines(options: {
|
|
|
26
26
|
home: string;
|
|
27
27
|
projectTrusted: boolean;
|
|
28
28
|
}): string[] {
|
|
29
|
-
const lines = [
|
|
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
|
|
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
|
-
: !
|
|
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
|
-
: !
|
|
63
|
+
: !tavilyUrlValid
|
|
63
64
|
? "TAVILY_API_URL 格式无效"
|
|
64
|
-
: "
|
|
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
|
-
|
|
75
|
-
lines.push(
|
|
72
|
+
lines.unshift(
|
|
76
73
|
selectedPath
|
|
77
|
-
?
|
|
78
|
-
:
|
|
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
|
}
|