@ycodium-ai/agent-pi 0.0.0-stage → 0.2.3576

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 CHANGED
@@ -1,3 +1,100 @@
1
- # Temporary Holding Version
1
+ # @ycodium-ai/agent-pi
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Run Pi in Ycodium with your own model API service. Choose Chat Completions,
4
+ OpenAI Responses or Anthropic Messages, then configure the API base URL, API key
5
+ and model ID in Settings → Providers when adding or editing a Pi
6
+ instance. The connection form is available on Web and Desktop; Mobile uses
7
+ instances configured on the connected machine.
8
+
9
+ ## Requirements
10
+
11
+ Install Pi 1.4.0 or newer on the machine running Ycodium:
12
+
13
+ ```sh
14
+ npm install -g @earendil-works/pi-coding-agent
15
+ ```
16
+
17
+ Pi must be on that machine's `PATH`, or its executable path must be set under the
18
+ instance's advanced settings. Choose the request protocol supported by your service.
19
+ For Chat Completions or OpenAI Responses, supply the API base URL, usually ending
20
+ in `/v1`, without `/chat/completions` or `/responses`. For Anthropic Messages,
21
+ supply the service root or a base URL ending in `/v1`; the runtime resolves the
22
+ request route to `/v1/messages`. URLs must not contain embedded credentials,
23
+ query parameters or a fragment. The model ID must be accepted by that service. The instance exposes
24
+ that one configured model; its readiness check does not make a model request or
25
+ verify API credentials. The first request checks access to the service.
26
+
27
+ ## Install
28
+
29
+ The plugin ships with Ycodium. To restore or install the package explicitly:
30
+
31
+ ```sh
32
+ ycodium plugin add @ycodium-ai/agent-pi
33
+ ```
34
+
35
+ Disabling or uninstalling it stops the plugin's processes. Saved instance settings
36
+ and session files remain. Uninstalling a bundled plugin records the removal, so a
37
+ server restart does not restore it; reinstall the package to restore it.
38
+
39
+ ## Connection and sessions
40
+
41
+ The form stores the API key as the sensitive `YCODIUM_PI_API_KEY` environment
42
+ variable. Existing keys are masked; leaving the masked field empty preserves the
43
+ saved key. Enter a new key to replace it. The URL and model are stored in the
44
+ instance's `apiBaseUrl` and `model` configuration fields. The optional `apiProtocol`
45
+ field selects `openai-completions`, `openai-responses` or `anthropic-messages`.
46
+ Connections without that field use Chat Completions. An unknown saved protocol
47
+ is shown as invalid and must be explicitly replaced. Advanced configurations
48
+ can also supply `YCODIUM_PI_API_BASE_URL`.
49
+
50
+ The plugin starts `pi --mode rpc` through Ycodium's managed process API. It
51
+ provides temporary model and MCP configuration, and enables Pi's built-in MCP and tool-search
52
+ extensions. MCP tools are discovered on demand rather than all advertised in the first request.
53
+ Native session files live in a directory specific
54
+ to the instance under `~/.pi/ycodium/`, unless a session directory is configured
55
+ under advanced settings. Keep that directory stable to resume existing sessions;
56
+ use a separate directory for each instance.
57
+
58
+ Pi creates its history file lazily. A thread stopped before its first prompt may have no saved
59
+ history and require a new thread. Missing saved history is reported as an error; the integration
60
+ does not silently replace it with an empty conversation.
61
+
62
+ Pi runs with full process permissions. It provides no sandbox or native per-tool
63
+ approval flow. Context compaction and steering use native RPC operations. This
64
+ integration does not offer OAuth, an auxiliary text-generation assistant,
65
+ mid-session model switching, inline image attachments, or a guarantee that an
66
+ interrupted tool's external side effects have stopped. MCP transports are stdio
67
+ and HTTP; legacy SSE is unsupported.
68
+
69
+ ## Developing
70
+
71
+ ```sh
72
+ vp run --filter @ycodium-ai/agent-pi test
73
+ vp run --filter @ycodium-ai/agent-pi typecheck
74
+ vp run --filter @ycodium-ai/agent-pi build
75
+ ```
76
+
77
+ The build emits `dist/index.js` and `assets/ui.js`. The settings view uses the
78
+ public provider-settings extension, the host's React and its shared input
79
+ primitive. English and Simplified Chinese translations ship in `locales/`.
80
+ The icon is a generic Pi symbol drawn for this package.
81
+
82
+ To include the native RPC integration test, set `YCODIUM_PI_TEST_CLI_ENTRY` to the absolute path
83
+ of an installed Pi package's `dist/bundle/cli.js` before running the package tests. The test uses
84
+ local model and MCP fixtures only; it makes no external model requests. Without that variable,
85
+ the native integration test is skipped.
86
+
87
+ Every opt-in native CLI launch passes through the standard wire recorder. Tests print the private
88
+ temporary directory containing its raw recordings; those files require scrubbing before sharing.
89
+ Real-model recording uses the web UI through
90
+ [`scripts/ui-probe/pi-recording.ts`](../../scripts/ui-probe/pi-recording.ts). The
91
+ [recording runbook](../../docs/operations/recording-real-sessions.md#pi真实界面录制与导出) owns setup,
92
+ credential handling and scrubbed export. The four real UI recording assets in the
93
+ [scenario manifest](test/recording-scenarios.json) cover a plain turn, resumed history, a native tool
94
+ call and genuine Anthropic thinking. They were recorded through the real web UI with Pi 1.4.0;
95
+ provenance, scrub and replay tests read the saved protocol files. Approval is explicitly unsupported
96
+ because Pi has no native per-tool approval flow.
97
+
98
+ ## License
99
+
100
+ UNLICENSED.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#e07a30"/><path d="M7 10h19v3h-4v10c0 1 .5 2 2 2h2v3h-3c-3 0-5-2-5-5V13h-5v8c0 4-2 6-5 7l-1-3c2-1 3-2 3-5v-7H7z" fill="#fff"/></svg>
package/assets/ui.js ADDED
@@ -0,0 +1,66 @@
1
+ var C=window.__ycodiumPluginHost.modules?.["@ycodium-ai/plugin-api/provider-settings"];if(!C)throw new Error("This plugin needs a newer Ycodium app. Update the app.");var{PROVIDER_SETTINGS_VIEW_CAPABILITY:_,accountDirectoryCandidates:G,accountDirectorySlug:W,accountProbeHasSignedInAccount:q,allocateAccountDirectory:J,providerInstanceAccountPaths:Q}=C;var j=window.__ycodiumPluginHost.react;var{Activity:Z,Children:ee,Component:te,Fragment:oe,Profiler:ne,PureComponent:re,StrictMode:ie,Suspense:ae,__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE:se,__COMPILER_RUNTIME:ce,act:pe,cache:le,cacheSignal:ue,captureOwnerStack:de,cloneElement:me,createContext:b,createElement:w,createRef:ge,forwardRef:fe,isValidElement:Pe,lazy:ye,memo:we,startTransition:ve,unstable_useCacheRefresh:he,use:Se,useActionState:Ie,useCallback:xe,useContext:E,useDebugValue:Te,useDeferredValue:Ce,useEffect:A,useEffectEvent:_e,useId:R,useImperativeHandle:be,useInsertionEffect:Ee,useLayoutEffect:Ae,useMemo:Re,useOptimistic:Ne,useReducer:Ue,useRef:ke,useState:Oe,useSyncExternalStore:Le,useTransition:Be,version:De}=j;var N=b(null),U=N.Provider;function l(){let t=E(N);if(t===null)throw new Error("Pi UI has not been registered.");return t}var v=["openai-completions","openai-responses","anthropic-messages"],k="openai-completions";function u(t){return v.some(e=>e===t)}var h="connection",f="YCODIUM_PI_API_KEY";function S(t){try{let e=new URL(t.trim());return(e.protocol==="http:"||e.protocol==="https:")&&!e.username&&!e.password&&!e.search&&!e.hash}catch{return!1}}function O(t){let{apiProtocol:e,apiBaseUrl:i,model:p}=t.config,n=t.environment.find(a=>a.name===f);return(e===void 0||u(e))&&typeof i=="string"&&S(i)&&typeof p=="string"&&p.trim().length>0&&n!==void 0&&(n.valueRedacted===!0||n.value.trim().length>0)}function L(t,e){let i=t.trim();return!i&&e?{}:{environment:[{name:f,value:i,sensitive:!0}]}}function I(t){return w(l().ui.DraftInput,t)}function d(t){return(e=>w(l().ui[t],{...e,className:`ycodium-pi-ui ${typeof e.className=="string"?e.className:""}`}))}var B=d("Select"),D=d("SelectTrigger"),H=d("SelectPopup"),K=d("SelectItem"),V=d("SelectValue");function $(){let t=l().i18n.usePluginTranslation();return{t:(e,i)=>t(e,i)}}var{jsx:r,jsxs:c,Fragment:qe}=window.__ycodiumPluginHost.jsxRuntime;function M({instance:t,readOnly:e,onReport:i,onChange:p}){let{t:n}=$(),a=R(),P=O(t),x=P?n("connectionNotVerified"):void 0;A(()=>i({complete:P,hidesSignIn:!0,signInAfterAdd:!1,notice:x}),[P,x,i]);let T=t.environment.find(o=>o.name===f),m=T?.valueRedacted===!0,y=t.config.apiProtocol,s=y===void 0?k:u(y)?y:null;return c("div",{className:"ycodium-pi-ui grid min-w-0 gap-4",children:[c("div",{className:"grid min-w-0 gap-1",children:[r("span",{id:`${a}-protocol-label`,className:"text-control-4 font-medium",children:n("form.apiProtocol.title")}),c(B,{value:s,disabled:e,onValueChange:o=>{!e&&u(o)&&p({config:{apiProtocol:o}})},children:[r(D,{"aria-labelledby":`${a}-protocol-label`,"aria-describedby":`${a}-protocol-description`,"aria-invalid":s===null,children:r(V,{children:n(s===null?"connection.protocolUnknown":`connection.protocols.${s}`)})}),r(H,{children:v.map(o=>r(K,{value:o,children:n(`connection.protocols.${o}`)},o))})]}),r("span",{id:`${a}-protocol-description`,className:s===null?"text-ui-sm text-destructive-foreground":"text-ui-sm text-muted-foreground",children:n(s===null?"connection.invalidProtocol":"form.apiProtocol.description")})]}),["apiBaseUrl","model"].map(o=>c("label",{className:"grid min-w-0 gap-1",children:[r("span",{className:"text-control-4 font-medium",children:n(`form.${o}.title`)}),r(I,{value:typeof t.config[o]=="string"?t.config[o]:"",onCommit:g=>{e||p({config:{[o]:g.trim()}})},validate:g=>(o==="apiBaseUrl"?S(g):g.trim().length>0)?null:n(o==="apiBaseUrl"?"connection.invalidUrl":"connection.modelRequired"),disabled:e,autoComplete:"off",spellCheck:!1,"aria-label":n(`form.${o}.title`),"aria-describedby":`${a}-${o}-description`}),r("span",{id:`${a}-${o}-description`,className:"text-ui-sm text-muted-foreground",children:n(o==="apiBaseUrl"&&s!==null?`connection.baseUrlGuidance.${s}`:`form.${o}.description`)})]},o)),c("label",{className:"grid min-w-0 gap-1",children:[r("span",{className:"text-control-4 font-medium",children:n("connection.key")}),r(I,{type:"password",autoComplete:"off",spellCheck:!1,disabled:e,"aria-label":n("connection.key"),value:m?"":T?.value??"",placeholder:m?n("connection.stored"):void 0,validate:o=>m||o.trim()?null:n("connection.keyRequired"),onCommit:o=>{e||p(L(o,m))}})]})]})}var Y=`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
+ @layer properties, theme, base, components, utilities;
3
+ @layer properties;
4
+ @layer utilities {
5
+ @scope (.ycodium-pi-ui) {
6
+ .relative:where(:scope, *) {
7
+ position: relative;
8
+ }
9
+ .container:where(:scope, *) {
10
+ width: 100%;
11
+ @media (width >= 40rem) {
12
+ max-width: 40rem;
13
+ }
14
+ @media (width >= 48rem) {
15
+ max-width: 48rem;
16
+ }
17
+ @media (width >= 64rem) {
18
+ max-width: 64rem;
19
+ }
20
+ @media (width >= 80rem) {
21
+ max-width: 80rem;
22
+ }
23
+ @media (width >= 96rem) {
24
+ max-width: 96rem;
25
+ }
26
+ }
27
+ .grid:where(:scope, *) {
28
+ display: grid;
29
+ }
30
+ .min-w-0:where(:scope, *) {
31
+ min-width: calc(var(--spacing, 0.25rem) * 0);
32
+ }
33
+ .gap-1:where(:scope, *) {
34
+ gap: calc(var(--spacing, 0.25rem) * 1);
35
+ }
36
+ .gap-4:where(:scope, *) {
37
+ gap: calc(var(--spacing, 0.25rem) * 4);
38
+ }
39
+ .text-control-4:where(:scope, *) {
40
+ font-size: var(--control-text-4);
41
+ line-height: var(--tw-leading, var(--control-leading-4));
42
+ }
43
+ .text-ui-sm:where(:scope, *) {
44
+ font-size: var(--ui-text-sm);
45
+ line-height: var(--tw-leading, var(--ui-leading-sm));
46
+ }
47
+ .font-medium:where(:scope, *) {
48
+ --tw-font-weight: var(--font-weight-medium, 500);
49
+ font-weight: var(--font-weight-medium, 500);
50
+ }
51
+ .text-destructive-foreground:where(:scope, *) {
52
+ color: var(--destructive-foreground);
53
+ }
54
+ .text-muted-foreground:where(:scope, *) {
55
+ color: var(--contrast-muted-foreground);
56
+ }
57
+ }
58
+ }
59
+ @layer properties {
60
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
61
+ *, ::before, ::after, ::backdrop {
62
+ --tw-font-weight: initial;
63
+ }
64
+ }
65
+ }
66
+ `;function F(t){let e=i=>c(U,{value:t,children:[r("style",{children:Y}),r(M,{...i})]});return{pages:{},panels:{},views:{[h]:e},viewCapabilities:{[h]:[_]}}}export{F as default};