@docyrus/docyrus 0.0.25 → 0.0.26
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 +34 -1
- package/agent-loader.js +249 -45
- package/agent-loader.js.map +3 -3
- package/main.js +166 -152
- package/main.js.map +4 -4
- package/package.json +2 -1
- package/resources/pi-agent/extensions/architect.ts +18 -34
- package/resources/pi-agent/extensions/plan.ts +1197 -0
- package/resources/pi-agent/shared/askUserProtocol.ts +248 -0
- package/server-loader.js +1446 -60
- package/server-loader.js.map +4 -4
package/README.md
CHANGED
|
@@ -219,5 +219,38 @@ Inside the interactive agent TUI:
|
|
|
219
219
|
```bash
|
|
220
220
|
/login # browser or API-key provider chooser
|
|
221
221
|
/logout # remove saved provider configuration
|
|
222
|
-
/
|
|
222
|
+
/plan [task] # start a planning-only branch and keep the current plan in .docyrus/plans/
|
|
223
|
+
/end-plan # leave the planning branch, summarize it, and return to the original branch
|
|
224
|
+
/end-architect # alias for leaving an architect planning session
|
|
225
|
+
/plan-policy # show the effective planning-model policy and the resolved planning model
|
|
226
|
+
/architect [brief] # discover tenant data sources and generate local planning artifacts under ./docyrus/architecture/
|
|
223
227
|
```
|
|
228
|
+
|
|
229
|
+
When running through `docyrus server`, `/plan` and `/architect` can be sent through `/api/chat` as normal chat input. If clarification is needed, the server emits a synthetic `ask_user` client tool. Frontends using AI SDK `useChat` should render that tool and submit structured answers back with `addToolOutput`.
|
|
230
|
+
|
|
231
|
+
Optional planning-model policy:
|
|
232
|
+
|
|
233
|
+
- Project config: `<project>/.pi/plan-policy.json`
|
|
234
|
+
- Global config: `~/.pi/agent/plan-policy.json`
|
|
235
|
+
- Project config takes precedence over global config
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"models": [
|
|
242
|
+
{ "model": "anthropic/claude-sonnet-4", "thinkingLevel": "high" },
|
|
243
|
+
"openai-codex/gpt-5.3-codex"
|
|
244
|
+
],
|
|
245
|
+
"profiles": {
|
|
246
|
+
"codex-implementation": {
|
|
247
|
+
"match": "openai-codex/gpt-5.3-codex",
|
|
248
|
+
"models": [
|
|
249
|
+
{ "model": "anthropic/claude-sonnet-4", "thinkingLevel": "medium" }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`match` is the exact implementation-time `provider/modelId` active when `/plan` starts. The `models` array is an ordered fallback chain. If no configured planning model resolves, `/plan` stays on the current session model.
|
package/agent-loader.js
CHANGED
|
@@ -1605,6 +1605,188 @@ function getProviderLabel(providerId) {
|
|
|
1605
1605
|
return toLabel(providerId);
|
|
1606
1606
|
}
|
|
1607
1607
|
|
|
1608
|
+
// src/agent/authInteractivity.ts
|
|
1609
|
+
function toSelectOptions(values) {
|
|
1610
|
+
return values.map((value) => ({ label: value, value }));
|
|
1611
|
+
}
|
|
1612
|
+
function getProviderFormFields(params) {
|
|
1613
|
+
const provider = params.provider;
|
|
1614
|
+
switch (provider.flow) {
|
|
1615
|
+
case "custom-openai":
|
|
1616
|
+
return [
|
|
1617
|
+
{
|
|
1618
|
+
name: "baseUrl",
|
|
1619
|
+
title: "Custom OpenAI base URL",
|
|
1620
|
+
required: true,
|
|
1621
|
+
component: "text",
|
|
1622
|
+
placeholder: "https://my-proxy.example.com/v1"
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
name: "apiKey",
|
|
1626
|
+
title: `${provider.label} API key`,
|
|
1627
|
+
required: true,
|
|
1628
|
+
component: "password"
|
|
1629
|
+
},
|
|
1630
|
+
{
|
|
1631
|
+
name: "modelId",
|
|
1632
|
+
title: "Model ID",
|
|
1633
|
+
required: true,
|
|
1634
|
+
component: "text",
|
|
1635
|
+
placeholder: "gpt-4o"
|
|
1636
|
+
}
|
|
1637
|
+
];
|
|
1638
|
+
case "azure-openai-responses":
|
|
1639
|
+
return [
|
|
1640
|
+
{
|
|
1641
|
+
name: "apiKey",
|
|
1642
|
+
title: "Azure OpenAI API key",
|
|
1643
|
+
required: true,
|
|
1644
|
+
component: "password"
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
name: "configMode",
|
|
1648
|
+
title: "Azure OpenAI configuration",
|
|
1649
|
+
required: true,
|
|
1650
|
+
defaultValue: "base-url",
|
|
1651
|
+
component: "select",
|
|
1652
|
+
options: [
|
|
1653
|
+
{ label: "Use a base URL", value: "base-url" },
|
|
1654
|
+
{ label: "Use an Azure resource name", value: "resource-name" }
|
|
1655
|
+
]
|
|
1656
|
+
},
|
|
1657
|
+
{
|
|
1658
|
+
name: "baseUrl",
|
|
1659
|
+
title: "Azure OpenAI base URL",
|
|
1660
|
+
required: true,
|
|
1661
|
+
component: "text",
|
|
1662
|
+
placeholder: "https://my-resource.openai.azure.com/openai/v1",
|
|
1663
|
+
dependsOn: { field: "configMode", equals: "base-url" }
|
|
1664
|
+
},
|
|
1665
|
+
{
|
|
1666
|
+
name: "resourceName",
|
|
1667
|
+
title: "Azure resource name",
|
|
1668
|
+
required: true,
|
|
1669
|
+
component: "text",
|
|
1670
|
+
placeholder: "my-resource",
|
|
1671
|
+
dependsOn: { field: "configMode", equals: "resource-name" }
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
name: "modelId",
|
|
1675
|
+
title: "Model ID",
|
|
1676
|
+
required: true,
|
|
1677
|
+
component: params.modelIdsByProvider[provider.id]?.length ? "select" : "text",
|
|
1678
|
+
options: params.modelIdsByProvider[provider.id]?.length ? toSelectOptions(params.modelIdsByProvider[provider.id]) : void 0,
|
|
1679
|
+
defaultValue: params.modelIdsByProvider[provider.id]?.[0] || "gpt-4.1"
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
name: "deploymentName",
|
|
1683
|
+
title: "Deployment name",
|
|
1684
|
+
component: "text",
|
|
1685
|
+
placeholder: "gpt-4.1"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
name: "apiVersion",
|
|
1689
|
+
title: "API version",
|
|
1690
|
+
component: "text",
|
|
1691
|
+
defaultValue: "2025-03-01-preview",
|
|
1692
|
+
placeholder: "2025-03-01-preview"
|
|
1693
|
+
}
|
|
1694
|
+
];
|
|
1695
|
+
case "amazon-bedrock":
|
|
1696
|
+
return [
|
|
1697
|
+
{
|
|
1698
|
+
name: "authMode",
|
|
1699
|
+
title: "Amazon Bedrock authentication",
|
|
1700
|
+
required: true,
|
|
1701
|
+
defaultValue: "profile",
|
|
1702
|
+
component: "select",
|
|
1703
|
+
options: [
|
|
1704
|
+
{ label: "Use an AWS profile", value: "profile" },
|
|
1705
|
+
{ label: "Paste AWS access keys", value: "access-keys" }
|
|
1706
|
+
]
|
|
1707
|
+
},
|
|
1708
|
+
{
|
|
1709
|
+
name: "profile",
|
|
1710
|
+
title: "AWS profile name",
|
|
1711
|
+
required: true,
|
|
1712
|
+
defaultValue: "default",
|
|
1713
|
+
component: "text",
|
|
1714
|
+
placeholder: "default",
|
|
1715
|
+
dependsOn: { field: "authMode", equals: "profile" }
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
name: "accessKeyId",
|
|
1719
|
+
title: "AWS access key ID",
|
|
1720
|
+
required: true,
|
|
1721
|
+
component: "text",
|
|
1722
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
name: "secretAccessKey",
|
|
1726
|
+
title: "AWS secret access key",
|
|
1727
|
+
required: true,
|
|
1728
|
+
component: "password",
|
|
1729
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
name: "sessionToken",
|
|
1733
|
+
title: "AWS session token",
|
|
1734
|
+
component: "password",
|
|
1735
|
+
dependsOn: { field: "authMode", equals: "access-keys" }
|
|
1736
|
+
},
|
|
1737
|
+
{
|
|
1738
|
+
name: "region",
|
|
1739
|
+
title: "AWS region",
|
|
1740
|
+
required: true,
|
|
1741
|
+
defaultValue: "us-east-1",
|
|
1742
|
+
component: "text",
|
|
1743
|
+
placeholder: "us-east-1"
|
|
1744
|
+
},
|
|
1745
|
+
{
|
|
1746
|
+
name: "modelId",
|
|
1747
|
+
title: "Bedrock model ID",
|
|
1748
|
+
required: true,
|
|
1749
|
+
component: params.modelIdsByProvider[provider.id]?.length ? "select" : "text",
|
|
1750
|
+
options: params.modelIdsByProvider[provider.id]?.length ? toSelectOptions(params.modelIdsByProvider[provider.id]) : void 0,
|
|
1751
|
+
defaultValue: params.modelIdsByProvider[provider.id]?.[0] || "anthropic.claude-3-7-sonnet-20250219-v1:0"
|
|
1752
|
+
}
|
|
1753
|
+
];
|
|
1754
|
+
default:
|
|
1755
|
+
return [
|
|
1756
|
+
{
|
|
1757
|
+
name: "apiKey",
|
|
1758
|
+
title: `${provider.label} API key`,
|
|
1759
|
+
required: true,
|
|
1760
|
+
component: "password"
|
|
1761
|
+
}
|
|
1762
|
+
];
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
async function refreshSessionAfterCredentialChange(params) {
|
|
1766
|
+
params.modelRegistry.refresh();
|
|
1767
|
+
const availableModels = params.modelRegistry.getAvailable();
|
|
1768
|
+
const preferredModel = params.preferredProviderId && params.preferredModelId ? availableModels.find((model) => model.provider === params.preferredProviderId && model.id === params.preferredModelId) : params.preferredProviderId ? availableModels.find((model) => model.provider === params.preferredProviderId) : void 0;
|
|
1769
|
+
const currentModel = params.session.model;
|
|
1770
|
+
const currentModelStillAvailable = currentModel ? availableModels.some((model) => model.provider === currentModel.provider && model.id === currentModel.id) : false;
|
|
1771
|
+
if (currentModel && !currentModelStillAvailable) {
|
|
1772
|
+
if (preferredModel) {
|
|
1773
|
+
await params.session.setModel(preferredModel);
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (availableModels[0]) {
|
|
1777
|
+
await params.session.setModel(availableModels[0]);
|
|
1778
|
+
}
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
if (!currentModel && preferredModel) {
|
|
1782
|
+
await params.session.setModel(preferredModel);
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
if (!currentModel && availableModels[0]) {
|
|
1786
|
+
await params.session.setModel(availableModels[0]);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1608
1790
|
// src/agent/interactiveAuth.ts
|
|
1609
1791
|
function modeAsAny(mode) {
|
|
1610
1792
|
return mode;
|
|
@@ -1674,43 +1856,37 @@ async function showInput(mode, title, placeholder) {
|
|
|
1674
1856
|
}
|
|
1675
1857
|
async function refreshAfterCredentialChange(mode, preferredProviderId, preferredModelId) {
|
|
1676
1858
|
const modeAny = modeAsAny(mode);
|
|
1677
|
-
|
|
1859
|
+
await refreshSessionAfterCredentialChange({
|
|
1860
|
+
session: modeAny.session,
|
|
1861
|
+
modelRegistry: modeAny.session.modelRegistry,
|
|
1862
|
+
preferredProviderId,
|
|
1863
|
+
preferredModelId
|
|
1864
|
+
});
|
|
1678
1865
|
await modeAny.updateAvailableProviderCount?.();
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
await modeAny.session.setModel(preferredModel);
|
|
1686
|
-
return;
|
|
1687
|
-
}
|
|
1688
|
-
if (availableModels[0]) {
|
|
1689
|
-
await modeAny.session.setModel(availableModels[0]);
|
|
1690
|
-
}
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1693
|
-
if (!currentModel && preferredModel) {
|
|
1694
|
-
await modeAny.session.setModel(preferredModel);
|
|
1695
|
-
return;
|
|
1696
|
-
}
|
|
1697
|
-
if (!currentModel && availableModels[0]) {
|
|
1698
|
-
await modeAny.session.setModel(availableModels[0]);
|
|
1699
|
-
}
|
|
1866
|
+
}
|
|
1867
|
+
function getField(provider, modelIdsByProvider, fieldName) {
|
|
1868
|
+
return getProviderFormFields({
|
|
1869
|
+
provider,
|
|
1870
|
+
modelIdsByProvider
|
|
1871
|
+
}).find((field) => field.name === fieldName);
|
|
1700
1872
|
}
|
|
1701
1873
|
async function runApiKeyProviderFlow(mode, dependencies, provider) {
|
|
1702
1874
|
const modeAny = modeAsAny(mode);
|
|
1875
|
+
const field = (name) => getField(provider, dependencies.modelIdsByProvider, name);
|
|
1703
1876
|
switch (provider.flow) {
|
|
1704
1877
|
case "custom-openai": {
|
|
1705
|
-
const
|
|
1878
|
+
const baseUrlField = field("baseUrl");
|
|
1879
|
+
const apiKeyField = field("apiKey");
|
|
1880
|
+
const modelIdField = field("modelId");
|
|
1881
|
+
const baseUrl = await showInput(mode, baseUrlField?.title || "Custom OpenAI base URL", baseUrlField?.placeholder);
|
|
1706
1882
|
if (!baseUrl) {
|
|
1707
1883
|
return;
|
|
1708
1884
|
}
|
|
1709
|
-
const apiKey = await showInput(mode, "Custom OpenAI API key");
|
|
1885
|
+
const apiKey = await showInput(mode, apiKeyField?.title || "Custom OpenAI API key");
|
|
1710
1886
|
if (!apiKey) {
|
|
1711
1887
|
return;
|
|
1712
1888
|
}
|
|
1713
|
-
const modelId = await showInput(mode, "Model ID",
|
|
1889
|
+
const modelId = await showInput(mode, modelIdField?.title || "Model ID", modelIdField?.placeholder);
|
|
1714
1890
|
if (!modelId) {
|
|
1715
1891
|
return;
|
|
1716
1892
|
}
|
|
@@ -1725,22 +1901,33 @@ async function runApiKeyProviderFlow(mode, dependencies, provider) {
|
|
|
1725
1901
|
return;
|
|
1726
1902
|
}
|
|
1727
1903
|
case "azure-openai-responses": {
|
|
1728
|
-
const
|
|
1904
|
+
const apiKeyField = field("apiKey");
|
|
1905
|
+
const configModeField = field("configMode");
|
|
1906
|
+
const baseUrlField = field("baseUrl");
|
|
1907
|
+
const resourceNameField = field("resourceName");
|
|
1908
|
+
const modelIdField = field("modelId");
|
|
1909
|
+
const deploymentNameField = field("deploymentName");
|
|
1910
|
+
const apiVersionField = field("apiVersion");
|
|
1911
|
+
const apiKey = await showInput(mode, apiKeyField?.title || "Azure OpenAI API key");
|
|
1729
1912
|
if (!apiKey) {
|
|
1730
1913
|
return;
|
|
1731
1914
|
}
|
|
1732
|
-
const configMode = await showSelection(
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1915
|
+
const configMode = await showSelection(
|
|
1916
|
+
mode,
|
|
1917
|
+
configModeField?.title || "Azure OpenAI configuration",
|
|
1918
|
+
(configModeField?.options ?? []).map((option) => ({
|
|
1919
|
+
value: option.value,
|
|
1920
|
+
label: option.label
|
|
1921
|
+
}))
|
|
1922
|
+
);
|
|
1736
1923
|
if (!configMode) {
|
|
1737
1924
|
return;
|
|
1738
1925
|
}
|
|
1739
1926
|
let baseUrl;
|
|
1740
1927
|
if (configMode === "base-url") {
|
|
1741
|
-
baseUrl = await showInput(mode, "Azure OpenAI base URL",
|
|
1928
|
+
baseUrl = await showInput(mode, baseUrlField?.title || "Azure OpenAI base URL", baseUrlField?.placeholder);
|
|
1742
1929
|
} else {
|
|
1743
|
-
const resourceName = await showInput(mode, "Azure resource name",
|
|
1930
|
+
const resourceName = await showInput(mode, resourceNameField?.title || "Azure resource name", resourceNameField?.placeholder);
|
|
1744
1931
|
if (resourceName) {
|
|
1745
1932
|
baseUrl = `https://${resourceName}.openai.azure.com/openai/v1`;
|
|
1746
1933
|
}
|
|
@@ -1748,12 +1935,15 @@ async function runApiKeyProviderFlow(mode, dependencies, provider) {
|
|
|
1748
1935
|
if (!baseUrl) {
|
|
1749
1936
|
return;
|
|
1750
1937
|
}
|
|
1751
|
-
const modelId = await
|
|
1938
|
+
const modelId = modelIdField?.options?.length ? await showSelection(mode, modelIdField.title, modelIdField.options.map((option) => ({
|
|
1939
|
+
value: option.value,
|
|
1940
|
+
label: option.label
|
|
1941
|
+
}))) : await showInput(mode, modelIdField?.title || "Model ID", modelIdField?.placeholder || modelIdField?.defaultValue);
|
|
1752
1942
|
if (!modelId) {
|
|
1753
1943
|
return;
|
|
1754
1944
|
}
|
|
1755
|
-
const deploymentName = await showInput(mode, "Deployment name
|
|
1756
|
-
const apiVersion = await showInput(mode, "API version
|
|
1945
|
+
const deploymentName = await showInput(mode, deploymentNameField?.title || "Deployment name", deploymentNameField?.placeholder || modelId);
|
|
1946
|
+
const apiVersion = await showInput(mode, apiVersionField?.title || "API version", apiVersionField?.placeholder || apiVersionField?.defaultValue);
|
|
1757
1947
|
const builtInModelIds = new Set(dependencies.modelIdsByProvider["azure-openai-responses"] || []);
|
|
1758
1948
|
await saveAzureConfig({
|
|
1759
1949
|
...dependencies,
|
|
@@ -1769,10 +1959,21 @@ async function runApiKeyProviderFlow(mode, dependencies, provider) {
|
|
|
1769
1959
|
return;
|
|
1770
1960
|
}
|
|
1771
1961
|
case "amazon-bedrock": {
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1962
|
+
const authModeField = field("authMode");
|
|
1963
|
+
const profileField = field("profile");
|
|
1964
|
+
const accessKeyIdField = field("accessKeyId");
|
|
1965
|
+
const secretAccessKeyField = field("secretAccessKey");
|
|
1966
|
+
const sessionTokenField = field("sessionToken");
|
|
1967
|
+
const regionField = field("region");
|
|
1968
|
+
const modelIdField = field("modelId");
|
|
1969
|
+
const authMode = await showSelection(
|
|
1970
|
+
mode,
|
|
1971
|
+
authModeField?.title || "Amazon Bedrock authentication",
|
|
1972
|
+
(authModeField?.options ?? []).map((option) => ({
|
|
1973
|
+
value: option.value,
|
|
1974
|
+
label: option.label
|
|
1975
|
+
}))
|
|
1976
|
+
);
|
|
1776
1977
|
if (!authMode) {
|
|
1777
1978
|
return;
|
|
1778
1979
|
}
|
|
@@ -1781,26 +1982,29 @@ async function runApiKeyProviderFlow(mode, dependencies, provider) {
|
|
|
1781
1982
|
let secretAccessKey;
|
|
1782
1983
|
let sessionToken;
|
|
1783
1984
|
if (authMode === "profile") {
|
|
1784
|
-
profile = await showInput(mode, "AWS profile name",
|
|
1985
|
+
profile = await showInput(mode, profileField?.title || "AWS profile name", profileField?.placeholder || profileField?.defaultValue);
|
|
1785
1986
|
if (!profile) {
|
|
1786
1987
|
return;
|
|
1787
1988
|
}
|
|
1788
1989
|
} else {
|
|
1789
|
-
accessKeyId = await showInput(mode, "AWS access key ID");
|
|
1990
|
+
accessKeyId = await showInput(mode, accessKeyIdField?.title || "AWS access key ID");
|
|
1790
1991
|
if (!accessKeyId) {
|
|
1791
1992
|
return;
|
|
1792
1993
|
}
|
|
1793
|
-
secretAccessKey = await showInput(mode, "AWS secret access key");
|
|
1994
|
+
secretAccessKey = await showInput(mode, secretAccessKeyField?.title || "AWS secret access key");
|
|
1794
1995
|
if (!secretAccessKey) {
|
|
1795
1996
|
return;
|
|
1796
1997
|
}
|
|
1797
|
-
sessionToken = await showInput(mode, "AWS session token
|
|
1998
|
+
sessionToken = await showInput(mode, sessionTokenField?.title || "AWS session token");
|
|
1798
1999
|
}
|
|
1799
|
-
const region = await showInput(mode, "AWS region",
|
|
2000
|
+
const region = await showInput(mode, regionField?.title || "AWS region", regionField?.placeholder || regionField?.defaultValue);
|
|
1800
2001
|
if (!region) {
|
|
1801
2002
|
return;
|
|
1802
2003
|
}
|
|
1803
|
-
const modelId = await
|
|
2004
|
+
const modelId = modelIdField?.options?.length ? await showSelection(mode, modelIdField.title, modelIdField.options.map((option) => ({
|
|
2005
|
+
value: option.value,
|
|
2006
|
+
label: option.label
|
|
2007
|
+
}))) : await showInput(mode, modelIdField?.title || "Bedrock model ID", modelIdField?.placeholder || modelIdField?.defaultValue);
|
|
1804
2008
|
if (!modelId) {
|
|
1805
2009
|
return;
|
|
1806
2010
|
}
|