@nmzpy/pi-ember-stack 0.1.6 → 0.2.2

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.
Files changed (36) hide show
  1. package/README.md +83 -83
  2. package/package.json +62 -48
  3. package/plugins/devin-auth/extensions/index.ts +68 -7
  4. package/plugins/devin-auth/src/cloud-direct/auth.ts +246 -246
  5. package/plugins/devin-auth/src/cloud-direct/catalog.ts +246 -246
  6. package/plugins/devin-auth/src/cloud-direct/chat.ts +1096 -1091
  7. package/plugins/devin-auth/src/cloud-direct/index.ts +41 -41
  8. package/plugins/devin-auth/src/cloud-direct/metadata.ts +78 -78
  9. package/plugins/devin-auth/src/cloud-direct/wire.ts +202 -202
  10. package/plugins/devin-auth/src/models.ts +42 -196
  11. package/plugins/devin-auth/src/oauth/register-user.ts +174 -174
  12. package/plugins/devin-auth/src/oauth/types.ts +71 -71
  13. package/plugins/devin-auth/src/stream.ts +1 -1
  14. package/plugins/index.ts +29 -6
  15. package/plugins/pi-compact-tools/index.ts +35 -192
  16. package/plugins/pi-compact-tools/renderer.ts +589 -0
  17. package/plugins/pi-custom-agents/index.ts +727 -373
  18. package/plugins/pi-custom-agents/questionnaire-tool.ts +14 -5
  19. package/plugins/pi-custom-agents/subagent/agents/coder.md +1 -0
  20. package/plugins/pi-custom-agents/subagent/agents/scout.md +16 -37
  21. package/plugins/pi-custom-agents/subagent/extensions/agents.ts +18 -1
  22. package/plugins/pi-custom-agents/subagent/extensions/index.ts +118 -226
  23. package/plugins/pi-custom-agents/subagent/extensions/model.ts +96 -96
  24. package/plugins/pi-custom-agents/subagent/extensions/render.ts +205 -1
  25. package/plugins/pi-custom-agents/subagent/extensions/runner.ts +260 -170
  26. package/plugins/pi-custom-agents/subagent/extensions/test/render.test.ts +145 -0
  27. package/plugins/pi-ember-fff/index.ts +975 -0
  28. package/plugins/pi-ember-fff/query.ts +247 -0
  29. package/plugins/pi-ember-fff/test/query.test.ts +222 -0
  30. package/plugins/pi-ember-fff/test/renderer.test.ts +727 -0
  31. package/plugins/pi-ember-tps/index.ts +144 -0
  32. package/plugins/pi-ember-ui/ember.json +99 -0
  33. package/plugins/pi-ember-ui/index.ts +805 -0
  34. package/plugins/pi-ember-ui/mode-colors.ts +196 -0
  35. package/tsconfig.json +2 -1
  36. package/plugins/pi-custom-agents/subagent/agents/architect.md +0 -56
package/README.md CHANGED
@@ -1,91 +1,91 @@
1
- # pi-ember-stack
2
-
3
- The Ember-owned pi package. It installs the complete Ember agent workflow as
4
- one package:
5
-
6
- - Primary modes: `/coder`, `/architect`, `/doctor`, `/orchestrator`, and
7
- `/ui-doctor`.
8
- - Inline `questionnaire` UI for decision-oriented questions. Agents are told to
9
- prefer it when they need a user choice.
10
- - A compact native `edit` renderer that shows the filename and a single
11
- `+N / -N` result row.
12
- - Vendored subagent support with bundled `coder` and `architect` definitions,
13
- plus the upstream bundled roles.
14
- - Bundled Devin auth/provider support, including OAuth, model catalog refresh,
15
- and streaming transport.
16
- - A self-contained `Ctrl+Space` mode-cycle shortcut and a footer showing the
17
- active mode, model, and thinking variant.
18
-
19
- ## Plugin registry
20
-
21
- The package has one top-level pi extension which dispatches to the internal
22
- plugins under `plugins/`. Ember projects enable them in `.pi/ember-stack.json`:
23
-
24
- ```json
25
- {
1
+ # pi-ember-stack
2
+
3
+ The Ember-owned pi package. It installs the complete Ember agent workflow as
4
+ one package:
5
+
6
+ - Primary modes: `/coder`, `/architect`, `/doctor`, `/orchestrator`, and
7
+ `/ui-doctor`.
8
+ - Inline `questionnaire` UI for decision-oriented questions. Agents are told to
9
+ prefer it when they need a user choice.
10
+ - A compact native `edit` renderer that shows the filename and a single
11
+ `+N / -N` result row.
12
+ - Vendored subagent support with bundled `coder` and `architect` definitions,
13
+ plus the upstream bundled roles.
14
+ - Bundled Devin auth/provider support, including OAuth, model catalog refresh,
15
+ and streaming transport.
16
+ - A self-contained `Ctrl+Space` mode-cycle shortcut and a footer showing the
17
+ active mode, model, and thinking variant.
18
+
19
+ ## Plugin registry
20
+
21
+ The package has one top-level pi extension which dispatches to the internal
22
+ plugins under `plugins/`. Ember projects enable them in `.pi/ember-stack.json`:
23
+
24
+ ```json
25
+ {
26
26
  "plugins": ["pi-compact-tools", "pi-custom-agents", "devin-auth"]
27
- }
28
- ```
29
-
30
- Remove a plugin ID to disable it, or use `/stack-plugins` to toggle one from
31
- the TUI. Restart pi after changing the list. The available plugins are:
32
-
27
+ }
28
+ ```
29
+
30
+ Remove a plugin ID to disable it, or use `/stack-plugins` to toggle one from
31
+ the TUI. Restart pi after changing the list. The available plugins are:
32
+
33
33
  - `pi-compact-tools`: collapsed native edit rendering.
34
34
  - `pi-custom-agents`: questionnaire UI, primary modes, plans, subagent tool, and bundled agent definitions.
35
- - `devin-auth`: Devin provider, OAuth, catalog refresh, and streaming.
36
-
37
- ## Project setup
38
-
39
- The Ember repository contains a project-local `.pi/settings.json` entry for:
40
-
41
- ```json
35
+ - `devin-auth`: Devin provider, OAuth, catalog refresh, and streaming.
36
+
37
+ ## Project setup
38
+
39
+ The Ember repository contains a project-local `.pi/settings.json` entry for:
40
+
41
+ ```json
42
42
  "npm:@nmzpy/pi-ember-stack@0.1.6"
43
- ```
44
-
45
- On a new clone, start pi from the project directory. Pi will ask for a
46
- one-time project trust decision before it installs the package into the
47
- project-local `.pi/npm/` directory. The same decision can be approved
48
- non-interactively with:
49
-
50
- ```text
51
- pi --approve
52
- ```
53
-
54
- Project trust is intentionally a user decision; a repository cannot safely
55
- bypass it. After trust, normal startup is just `pi` from the Ember directory.
56
-
57
- When a new version is intentionally released, update the pinned version in
58
- the project settings and run:
59
-
60
- ```text
61
- pi update --extensions
62
- ```
63
-
64
- Third-party utilities such as pi-fff and image paste remain separate package
65
- entries. Devin auth is now bundled as a stack plugin, but credentials and
66
- provider secrets stay in the machine-local pi configuration and are not part
67
- of this repository.
68
-
69
- ## Development
70
-
43
+ ```
44
+
45
+ On a new clone, start pi from the project directory. Pi will ask for a
46
+ one-time project trust decision before it installs the package into the
47
+ project-local `.pi/npm/` directory. The same decision can be approved
48
+ non-interactively with:
49
+
50
+ ```text
51
+ pi --approve
52
+ ```
53
+
54
+ Project trust is intentionally a user decision; a repository cannot safely
55
+ bypass it. After trust, normal startup is just `pi` from the Ember directory.
56
+
57
+ When a new version is intentionally released, update the pinned version in
58
+ the project settings and run:
59
+
60
+ ```text
61
+ pi update --extensions
62
+ ```
63
+
64
+ Third-party utilities such as pi-fff and image paste remain separate package
65
+ entries. Devin auth is now bundled as a stack plugin, but credentials and
66
+ provider secrets stay in the machine-local pi configuration and are not part
67
+ of this repository.
68
+
69
+ ## Development
70
+
71
71
  The package entrypoint is `plugins/index.ts`. Compact tools are under
72
72
  `plugins/pi-compact-tools/`, while questionnaire, primary modes, plans,
73
73
  subagents, and bundled agents are under `plugins/pi-custom-agents/`. Devin auth
74
74
  is under `plugins/devin-auth/`.
75
-
76
- Run the package typecheck with:
77
-
78
- ```text
79
- npm install
80
- npm run typecheck
81
- ```
82
-
83
- ## Release
84
-
85
- Run `./gacp.sh --release` to bump the patch version, typecheck, commit, tag,
86
- push, and publish the package to npm. This only publishes the package; update
87
- Ember's pinned package version separately when you want the project to install it.
88
-
89
- The package is cross-platform: bundled paths are resolved from `import.meta.url`
90
- and do not depend on a Windows home directory or the current working
91
- directory.
75
+
76
+ Run the package typecheck with:
77
+
78
+ ```text
79
+ npm install
80
+ npm run typecheck
81
+ ```
82
+
83
+ ## Release
84
+
85
+ Run `./gacp.sh --release` to bump the patch version, typecheck, commit, tag,
86
+ push, and publish the package to npm. This only publishes the package; update
87
+ Ember's pinned package version separately when you want the project to install it.
88
+
89
+ The package is cross-platform: bundled paths are resolved from `import.meta.url`
90
+ and do not depend on a Windows home directory or the current working
91
+ directory.
package/package.json CHANGED
@@ -1,49 +1,63 @@
1
- {
2
- "name": "@nmzpy/pi-ember-stack",
3
- "version": "0.1.6",
1
+ {
2
+ "name": "@nmzpy/pi-ember-stack",
3
+ "version": "0.2.2",
4
4
  "description": "Ember's configurable pi plugin stack with modes, questionnaire, compact edits, subagents, and Devin auth",
5
- "type": "module",
6
- "license": "MIT",
7
- "keywords": [
8
- "pi-package",
9
- "pi-extension",
10
- "ember"
11
- ],
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/nmzpy/pi-ember-stack.git"
15
- },
16
- "bugs": {
17
- "url": "https://github.com/nmzpy/pi-ember-stack/issues"
18
- },
19
- "scripts": {
20
- "typecheck": "tsc --noEmit"
21
- },
22
- "files": [
23
- "README.md",
24
- "ATTRIBUTION.md",
25
- "tsconfig.json",
26
- "plugins"
27
- ],
28
- "pi": {
29
- "extensions": [
30
- "./plugins/index.ts"
31
- ]
32
- },
33
- "peerDependencies": {
34
- "@earendil-works/pi-ai": "*",
35
- "@earendil-works/pi-agent-core": "*",
36
- "@earendil-works/pi-coding-agent": "*",
37
- "@earendil-works/pi-tui": "*",
38
- "typebox": "*"
39
- },
40
- "devDependencies": {
41
- "@earendil-works/pi-ai": "^0.80.6",
42
- "@earendil-works/pi-agent-core": "^0.80.6",
43
- "@earendil-works/pi-coding-agent": "^0.80.6",
44
- "@earendil-works/pi-tui": "^0.80.6",
45
- "@types/node": "^20.19.43",
46
- "typebox": "^1.3.1",
47
- "typescript": "^5.9.3"
48
- }
49
- }
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "pi-package",
9
+ "pi-extension",
10
+ "ember"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/nmzpy/pi-ember-stack.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/nmzpy/pi-ember-stack/issues"
18
+ },
19
+ "scripts": {
20
+ "typecheck": "tsc --noEmit",
21
+ "lint": "biome lint",
22
+ "format": "biome format --write",
23
+ "check": "biome check"
24
+ },
25
+ "files": [
26
+ "README.md",
27
+ "ATTRIBUTION.md",
28
+ "tsconfig.json",
29
+ "plugins"
30
+ ],
31
+ "exports": {
32
+ ".": "./plugins/index.ts",
33
+ "./renderer": "./plugins/pi-compact-tools/renderer.ts"
34
+ },
35
+ "pi": {
36
+ "extensions": [
37
+ "./plugins/index.ts"
38
+ ]
39
+ },
40
+ "dependencies": {
41
+ "@ff-labs/fff-node": "0.9.6",
42
+ "@sinclair/typebox": "^0.34.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@earendil-works/pi-agent-core": "*",
46
+ "@earendil-works/pi-ai": "*",
47
+ "@earendil-works/pi-coding-agent": "*",
48
+ "@earendil-works/pi-tui": "*",
49
+ "typebox": "*"
50
+ },
51
+ "devDependencies": {
52
+ "@biomejs/biome": "^2.5.3",
53
+ "@earendil-works/pi-agent-core": "^0.80.6",
54
+ "@earendil-works/pi-ai": "^0.80.6",
55
+ "@earendil-works/pi-coding-agent": "^0.80.6",
56
+ "@earendil-works/pi-tui": "^0.80.6",
57
+ "@ff-labs/fff-node": "0.9.6",
58
+ "@sinclair/typebox": "^0.34.0",
59
+ "@types/node": "^20.19.43",
60
+ "typebox": "^1.3.1",
61
+ "typescript": "^5.9.3"
62
+ }
63
+ }
@@ -5,7 +5,7 @@
5
5
  * - OAuth login via Windsurf's browser sign-in flow (`loginDevin`)
6
6
  * - A no-op token refresh (Windsurf api_keys are long-lived)
7
7
  * - Live model catalog fetch from Cognition's GetCascadeModelConfigs
8
- * after login, filtered to 11 wanted model families
8
+ * after login, surfacing every model the account has access to
9
9
  * - `/devin-refresh` command to manually re-fetch the catalog
10
10
  * - `/devin-status` command to check auth state
11
11
  * - `session_start` auto-fetch when already logged in
@@ -15,7 +15,8 @@
15
15
  * and auth are handled internally via the OAuth-issued api_key.
16
16
  */
17
17
 
18
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
18
+ import type { ExtensionAPI, ProviderModelConfig } from '@earendil-works/pi-coding-agent';
19
+ import { AuthStorage } from '@earendil-works/pi-coding-agent';
19
20
  import type {
20
21
  Api,
21
22
  Model,
@@ -24,7 +25,7 @@ import type {
24
25
  } from '@earendil-works/pi-ai';
25
26
  import { streamDevin } from '../src/stream.js';
26
27
  import { loginDevin } from '../src/oauth/login.js';
27
- import { buildLiveModels, FALLBACK_MODELS, DEFAULT_HOST } from '../src/models.js';
28
+ import { buildLiveModels, DEFAULT_HOST } from '../src/models.js';
28
29
  import { getCachedCatalog, clearCachedCatalog } from '../src/cloud-direct/catalog.js';
29
30
  import { DEFAULT_REGION } from '../src/oauth/types.js';
30
31
 
@@ -38,7 +39,34 @@ const PLACEHOLDER_BASE_URL = DEFAULT_HOST;
38
39
 
39
40
  let _pi: ExtensionAPI | null = null;
40
41
 
41
- function registerDevinProvider(pi: ExtensionAPI, models: typeof FALLBACK_MODELS): void {
42
+ /**
43
+ * Fetch the live catalog using credentials already in auth.json and register
44
+ * the resulting models. Called during the awaited factory load so devin
45
+ * models exist before pi flushes pending provider registrations and restores
46
+ * the session model.
47
+ *
48
+ * Reads auth.json directly via AuthStorage (the model registry is not bound
49
+ * yet during factory load). Silently no-ops when not signed in or the fetch
50
+ * fails — the session_start handler re-attempts once the registry is live.
51
+ */
52
+ async function primeCatalogFromStoredAuth(): Promise<void> {
53
+ if (!_pi) return;
54
+ try {
55
+ const authStorage = AuthStorage.create();
56
+ const apiKey = await authStorage.getApiKey(PROVIDER_ID, { includeFallback: false });
57
+ if (!apiKey) return;
58
+ const catalog = await getCachedCatalog(apiKey, DEFAULT_HOST);
59
+ const liveModels = buildLiveModels(catalog);
60
+ if (liveModels.length > 0) {
61
+ registerDevinProvider(_pi, liveModels);
62
+ }
63
+ } catch {
64
+ // Not signed in, network failure, or schema drift — keep the empty
65
+ // model list and let session_start retry once the registry is bound.
66
+ }
67
+ }
68
+
69
+ function registerDevinProvider(pi: ExtensionAPI, models: ProviderModelConfig[]): void {
42
70
  pi.registerProvider(PROVIDER_ID, {
43
71
  name: PROVIDER_NAME,
44
72
  api: API_IDENTIFIER,
@@ -58,7 +86,7 @@ function registerDevinProvider(pi: ExtensionAPI, models: typeof FALLBACK_MODELS)
58
86
  const liveModels = buildLiveModels(catalog);
59
87
  registerDevinProvider(_pi, liveModels);
60
88
  } catch {
61
- // keep static models if catalog fetch fails
89
+ // keep current models if catalog fetch fails
62
90
  }
63
91
  }
64
92
  return credentials;
@@ -80,9 +108,41 @@ function registerDevinProvider(pi: ExtensionAPI, models: typeof FALLBACK_MODELS)
80
108
  export default async function (pi: ExtensionAPI): Promise<void> {
81
109
  _pi = pi;
82
110
 
83
- registerDevinProvider(pi, FALLBACK_MODELS);
111
+ // Swallow AbortError unhandled rejections that arise when the user
112
+ // cancels an in-flight agent run (Escape during streaming). The agent's
113
+ // AbortController.abort() sets signal.reason to a DOMException
114
+ // [AbortError]; late rejections from fetch body streams, reader.cancel(),
115
+ // or the anySignal polyfill can surface as unhandled rejections that —
116
+ // on Node ≥15 with --unhandled-rejections=throw (the default in Node 25)
117
+ // — trigger pi's uncaughtException handler and crash the process.
118
+ // These are expected during cancellation, not real errors.
119
+ //
120
+ // Non-abort rejections are genuine bugs: temporarily remove the guard,
121
+ // re-emit so pi's crash handler surfaces them, then re-attach.
122
+ const abortRejectionHandler = (reason: unknown): void => {
123
+ const isAbort =
124
+ reason instanceof DOMException && reason.name === 'AbortError';
125
+ if (isAbort) return; // expected during cancellation
126
+ // Re-emit non-abort rejections so they are not silently swallowed.
127
+ process.off('unhandledRejection', abortRejectionHandler);
128
+ process.emit('unhandledRejection', reason, Promise.reject(reason));
129
+ process.on('unhandledRejection', abortRejectionHandler);
130
+ };
131
+ process.on('unhandledRejection', abortRejectionHandler);
132
+
133
+ // Register with an empty model list first so the provider (and OAuth
134
+ // login support) is known even before the catalog arrives. Then, if we
135
+ // already have credentials in auth.json, fetch the live catalog now —
136
+ // during the awaited factory load, before pi flushes pending provider
137
+ // registrations and restores the session model. This is what makes
138
+ // `devin/glm-5-2` resolvable on resume instead of falling back to the
139
+ // default provider with a "Could not restore model" warning.
140
+ registerDevinProvider(pi, []);
141
+ await primeCatalogFromStoredAuth();
84
142
 
85
143
  pi.on('session_start', async (_event, ctx) => {
144
+ // Re-prime in case credentials were added via /login since load, or
145
+ // the catalog TTL expired during a long-lived session.
86
146
  try {
87
147
  const apiKey = await ctx.modelRegistry.getApiKeyForProvider(PROVIDER_ID);
88
148
  if (apiKey && _pi) {
@@ -91,7 +151,7 @@ export default async function (pi: ExtensionAPI): Promise<void> {
91
151
  registerDevinProvider(_pi, liveModels);
92
152
  }
93
153
  } catch {
94
- // keep static models
154
+ // keep current models
95
155
  }
96
156
  });
97
157
 
@@ -137,5 +197,6 @@ export default async function (pi: ExtensionAPI): Promise<void> {
137
197
 
138
198
  pi.on('session_shutdown', async () => {
139
199
  _pi = null;
200
+ process.off('unhandledRejection', abortRejectionHandler);
140
201
  });
141
202
  }