@gonrocca/zero-pi 0.1.12 → 0.1.14
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/LICENSE +21 -21
- package/README.md +296 -258
- package/extensions/autotune-extension.ts +250 -250
- package/extensions/autotune.ts +558 -520
- package/extensions/conversation-resume.ts +400 -399
- package/extensions/provider-guard-extension.ts +195 -195
- package/extensions/provider-guard.ts +183 -183
- package/extensions/spec-merge-extension.ts +286 -286
- package/extensions/spec-merge.ts +373 -373
- package/extensions/startup-banner.ts +237 -237
- package/extensions/win-tree-kill.ts +114 -0
- package/extensions/working-phrases.ts +295 -295
- package/extensions/zero-models.ts +463 -333
- package/package.json +75 -73
- package/prompts/forge.md +34 -34
- package/prompts/orchestrator.md +292 -246
- package/prompts/phases/build.md +20 -20
- package/prompts/phases/explore.md +22 -22
- package/prompts/phases/plan.md +82 -82
- package/prompts/phases/veredicto.md +30 -30
- package/skills/{sdd-routing.md → sdd-routing/SKILL.md} +51 -51
- package/skills/{skill-loop.md → skill-loop/SKILL.md} +29 -29
- package/themes/zero-sdd.json +76 -76
|
@@ -1,195 +1,195 @@
|
|
|
1
|
-
// zero-pi — metered-provider guard, pi wiring.
|
|
2
|
-
//
|
|
3
|
-
// A thin pi extension that wires the pure decision logic in `provider-guard.ts`
|
|
4
|
-
// to the `model_select` hook. Whenever pi reports a model switch, this handler
|
|
5
|
-
// classifies it (in the pure module) and executes the resulting `GuardAction`:
|
|
6
|
-
// a silent no-op for subscription providers, a non-blocking `warning` for a
|
|
7
|
-
// metered provider with no equivalent (or a session `restore`), or — for a
|
|
8
|
-
// deliberate switch to a metered provider that has an equivalent — a `confirm`
|
|
9
|
-
// dialog offering to redirect to the subscription provider.
|
|
10
|
-
//
|
|
11
|
-
// All decisions live in `provider-guard.ts`; this file only declares the
|
|
12
|
-
// minimal pi-API surface it uses, hooks `model_select`, and performs the I/O
|
|
13
|
-
// (`confirm`/`notify`/`setModel`). Both `register` and the handler are wrapped
|
|
14
|
-
// in a swallowing `try/catch` — exactly like `autotune-extension.ts`, a failure
|
|
15
|
-
// must never break a pi session. The package stays dependency-free: only
|
|
16
|
-
// `provider-guard.ts` is imported (no `node:*` is needed here, no third party).
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
classifyModelSwitch,
|
|
20
|
-
redirectFailedMessage,
|
|
21
|
-
type ModelLike,
|
|
22
|
-
} from "./provider-guard.ts";
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Minimal local pi-API interfaces
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
//
|
|
28
|
-
// `provider-guard-extension.ts` declares only the slice of pi's API it uses,
|
|
29
|
-
// exactly like `autotune-extension.ts` and `zero-models.ts`. The guard never
|
|
30
|
-
// depends on pi's full type surface — just the shapes below.
|
|
31
|
-
|
|
32
|
-
/** The minimum a `Model` of pi must expose for the guard. Mirrors `ModelLike`
|
|
33
|
-
* of the pure module — `setModel`/the registry both speak this shape. */
|
|
34
|
-
interface PiModel {
|
|
35
|
-
provider: string;
|
|
36
|
-
id: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** The model registry slice the guard uses: a `find` that resolves a model by
|
|
40
|
-
* `(provider, id)` or returns `undefined` when no such model exists. */
|
|
41
|
-
interface PiModelRegistry {
|
|
42
|
-
find(provider: string, modelId: string): PiModel | undefined;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** The slice of pi's UI surface the guard uses. `confirm` may be sync or async
|
|
46
|
-
* (pi versions differ); both are handled uniformly with `await`. */
|
|
47
|
-
interface PiUI {
|
|
48
|
-
confirm(title: string, message: string): Promise<boolean> | boolean;
|
|
49
|
-
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** The context pi passes to a `model_select` handler. `modelRegistry` is
|
|
53
|
-
* optional — if absent the guard degrades to warn-only (it can no longer
|
|
54
|
-
* resolve equivalents) rather than breaking. */
|
|
55
|
-
interface PiModelSelectContext {
|
|
56
|
-
ui: PiUI;
|
|
57
|
-
modelRegistry?: PiModelRegistry;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** The `model_select` event pi emits on a model change. The guard only reads
|
|
61
|
-
* `model` and `source`; `source` is `set`/`cycle`/`restore` or, defensively,
|
|
62
|
-
* any other string a future pi version might report. */
|
|
63
|
-
interface PiModelSelectEvent {
|
|
64
|
-
type: "model_select";
|
|
65
|
-
model?: PiModel;
|
|
66
|
-
source?: "set" | "cycle" | "restore" | string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** The slice of pi's extension API the guard uses: `on` to hook events and
|
|
70
|
-
* `setModel` to apply a redirection. */
|
|
71
|
-
interface PiExtensionAPI {
|
|
72
|
-
on(
|
|
73
|
-
event: string,
|
|
74
|
-
handler: (event: PiModelSelectEvent, ctx: PiModelSelectContext) => void | Promise<void>,
|
|
75
|
-
): void;
|
|
76
|
-
setModel(model: PiModel): Promise<boolean>;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ---------------------------------------------------------------------------
|
|
80
|
-
// Handler
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The `model_select` guard handler.
|
|
85
|
-
*
|
|
86
|
-
* Runs entirely inside `register`'s swallowing `try/catch`, but is also wrapped
|
|
87
|
-
* again here so no failure of `find`/`confirm`/`notify`/`setModel` can ever
|
|
88
|
-
* propagate out of a pi session. The flow:
|
|
89
|
-
*
|
|
90
|
-
* 1. Validate `event`/`event.model` — a malformed event returns cleanly.
|
|
91
|
-
* 2. Build the injected `lookup` over `ctx.modelRegistry.find`, wrapped in a
|
|
92
|
-
* `try/catch` that returns `undefined`. If `modelRegistry` is absent,
|
|
93
|
-
* degrade to an always-`undefined` lookup (warn-only).
|
|
94
|
-
* 3. `classifyModelSwitch(model, source, lookup)` → a `GuardAction`.
|
|
95
|
-
* 4. Execute the action: `ignore` → no-op; `warn` → `notify(..., "warning")`;
|
|
96
|
-
* `offer-redirect` → `confirm`, and on a truthy answer `setModel` +
|
|
97
|
-
* `notify(..., "info")` (or the failure notice if `setModel` returns
|
|
98
|
-
* `false`). A falsy `confirm` leaves the user on the metered provider.
|
|
99
|
-
*/
|
|
100
|
-
async function handleModelSelect(
|
|
101
|
-
event: PiModelSelectEvent,
|
|
102
|
-
ctx: PiModelSelectContext,
|
|
103
|
-
pi: PiExtensionAPI,
|
|
104
|
-
): Promise<void> {
|
|
105
|
-
try {
|
|
106
|
-
// 1. Malformed event — no event, no model, or no UI to talk to: bail out.
|
|
107
|
-
if (!event || !event.model || !ctx || !ctx.ui || typeof ctx.ui.notify !== "function") {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 2. Build the injected lookup. `classifyModelSwitch` assumes a total
|
|
112
|
-
// lookup, so `find` is wrapped in a `try/catch` returning `undefined`.
|
|
113
|
-
// If `modelRegistry` is absent the guard cannot resolve equivalents —
|
|
114
|
-
// degrade to an always-`undefined` lookup so a metered provider still
|
|
115
|
-
// produces a warning rather than breaking.
|
|
116
|
-
const registry = ctx.modelRegistry;
|
|
117
|
-
const lookup = (provider: string, id: string): ModelLike | undefined => {
|
|
118
|
-
if (!registry || typeof registry.find !== "function") return undefined;
|
|
119
|
-
try {
|
|
120
|
-
return registry.find(provider, id);
|
|
121
|
-
} catch {
|
|
122
|
-
// A registry lookup failure degrades to "no equivalent", never throws.
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// 3. Classify the switch in the pure module.
|
|
128
|
-
const action = classifyModelSwitch(event.model, event.source, lookup);
|
|
129
|
-
|
|
130
|
-
// 4. Execute the action.
|
|
131
|
-
if (action.kind === "ignore") {
|
|
132
|
-
// Non-metered provider or malformed event — nothing to do.
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (action.kind === "warn") {
|
|
137
|
-
// Metered provider with no equivalent, or a session `restore` — a single
|
|
138
|
-
// non-blocking warning, no modal, no `setModel`.
|
|
139
|
-
ctx.ui.notify(action.message, "warning");
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// action.kind === "offer-redirect" — deliberate switch to a metered
|
|
144
|
-
// provider that has a subscription equivalent. Offer the redirect; the
|
|
145
|
-
// `confirm` dialog itself is the user's escape (AC 1.5).
|
|
146
|
-
const accepted = await ctx.ui.confirm(action.confirmTitle, action.confirmMessage);
|
|
147
|
-
if (!accepted) {
|
|
148
|
-
// User declined or cancelled — leave them on the metered provider, no
|
|
149
|
-
// second warning, no `setModel`.
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const applied = await pi.setModel(action.safeModel);
|
|
154
|
-
if (applied) {
|
|
155
|
-
// Redirection took effect — confirm it with an `info` notification.
|
|
156
|
-
ctx.ui.notify(action.redirectMessage, "info");
|
|
157
|
-
} else {
|
|
158
|
-
// `setModel` returned `false`: the switch did not apply. Do not claim
|
|
159
|
-
// "redirected" — emit the optional failure notice instead.
|
|
160
|
-
ctx.ui.notify(
|
|
161
|
-
redirectFailedMessage(action.safeModel.id, action.safeModel.provider),
|
|
162
|
-
"warning",
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
} catch {
|
|
166
|
-
// Any failure of the guard must never break a pi session.
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ---------------------------------------------------------------------------
|
|
171
|
-
// Registration
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* The pi extension entry point.
|
|
176
|
-
*
|
|
177
|
-
* pi calls this once when the extension loads. It wires the guard handler to
|
|
178
|
-
* the `model_select` event. The whole body is wrapped in a swallowing
|
|
179
|
-
* `try/catch`, and the handler is wrapped again, so neither registration nor a
|
|
180
|
-
* later failure can ever break a pi session. Called with no or an invalid
|
|
181
|
-
* `pi` (missing, or no `.on` function), it no-ops cleanly.
|
|
182
|
-
*/
|
|
183
|
-
export default function register(pi?: unknown): void {
|
|
184
|
-
try {
|
|
185
|
-
if (!pi || typeof (pi as PiExtensionAPI).on !== "function") {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const api = pi as PiExtensionAPI;
|
|
189
|
-
api.on("model_select", (event: PiModelSelectEvent, ctx: PiModelSelectContext): Promise<void> => {
|
|
190
|
-
return handleModelSelect(event, ctx, api);
|
|
191
|
-
});
|
|
192
|
-
} catch {
|
|
193
|
-
// Registration itself must never break a pi session.
|
|
194
|
-
}
|
|
195
|
-
}
|
|
1
|
+
// zero-pi — metered-provider guard, pi wiring.
|
|
2
|
+
//
|
|
3
|
+
// A thin pi extension that wires the pure decision logic in `provider-guard.ts`
|
|
4
|
+
// to the `model_select` hook. Whenever pi reports a model switch, this handler
|
|
5
|
+
// classifies it (in the pure module) and executes the resulting `GuardAction`:
|
|
6
|
+
// a silent no-op for subscription providers, a non-blocking `warning` for a
|
|
7
|
+
// metered provider with no equivalent (or a session `restore`), or — for a
|
|
8
|
+
// deliberate switch to a metered provider that has an equivalent — a `confirm`
|
|
9
|
+
// dialog offering to redirect to the subscription provider.
|
|
10
|
+
//
|
|
11
|
+
// All decisions live in `provider-guard.ts`; this file only declares the
|
|
12
|
+
// minimal pi-API surface it uses, hooks `model_select`, and performs the I/O
|
|
13
|
+
// (`confirm`/`notify`/`setModel`). Both `register` and the handler are wrapped
|
|
14
|
+
// in a swallowing `try/catch` — exactly like `autotune-extension.ts`, a failure
|
|
15
|
+
// must never break a pi session. The package stays dependency-free: only
|
|
16
|
+
// `provider-guard.ts` is imported (no `node:*` is needed here, no third party).
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
classifyModelSwitch,
|
|
20
|
+
redirectFailedMessage,
|
|
21
|
+
type ModelLike,
|
|
22
|
+
} from "./provider-guard.ts";
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Minimal local pi-API interfaces
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
//
|
|
28
|
+
// `provider-guard-extension.ts` declares only the slice of pi's API it uses,
|
|
29
|
+
// exactly like `autotune-extension.ts` and `zero-models.ts`. The guard never
|
|
30
|
+
// depends on pi's full type surface — just the shapes below.
|
|
31
|
+
|
|
32
|
+
/** The minimum a `Model` of pi must expose for the guard. Mirrors `ModelLike`
|
|
33
|
+
* of the pure module — `setModel`/the registry both speak this shape. */
|
|
34
|
+
interface PiModel {
|
|
35
|
+
provider: string;
|
|
36
|
+
id: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The model registry slice the guard uses: a `find` that resolves a model by
|
|
40
|
+
* `(provider, id)` or returns `undefined` when no such model exists. */
|
|
41
|
+
interface PiModelRegistry {
|
|
42
|
+
find(provider: string, modelId: string): PiModel | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The slice of pi's UI surface the guard uses. `confirm` may be sync or async
|
|
46
|
+
* (pi versions differ); both are handled uniformly with `await`. */
|
|
47
|
+
interface PiUI {
|
|
48
|
+
confirm(title: string, message: string): Promise<boolean> | boolean;
|
|
49
|
+
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The context pi passes to a `model_select` handler. `modelRegistry` is
|
|
53
|
+
* optional — if absent the guard degrades to warn-only (it can no longer
|
|
54
|
+
* resolve equivalents) rather than breaking. */
|
|
55
|
+
interface PiModelSelectContext {
|
|
56
|
+
ui: PiUI;
|
|
57
|
+
modelRegistry?: PiModelRegistry;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The `model_select` event pi emits on a model change. The guard only reads
|
|
61
|
+
* `model` and `source`; `source` is `set`/`cycle`/`restore` or, defensively,
|
|
62
|
+
* any other string a future pi version might report. */
|
|
63
|
+
interface PiModelSelectEvent {
|
|
64
|
+
type: "model_select";
|
|
65
|
+
model?: PiModel;
|
|
66
|
+
source?: "set" | "cycle" | "restore" | string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The slice of pi's extension API the guard uses: `on` to hook events and
|
|
70
|
+
* `setModel` to apply a redirection. */
|
|
71
|
+
interface PiExtensionAPI {
|
|
72
|
+
on(
|
|
73
|
+
event: string,
|
|
74
|
+
handler: (event: PiModelSelectEvent, ctx: PiModelSelectContext) => void | Promise<void>,
|
|
75
|
+
): void;
|
|
76
|
+
setModel(model: PiModel): Promise<boolean>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Handler
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The `model_select` guard handler.
|
|
85
|
+
*
|
|
86
|
+
* Runs entirely inside `register`'s swallowing `try/catch`, but is also wrapped
|
|
87
|
+
* again here so no failure of `find`/`confirm`/`notify`/`setModel` can ever
|
|
88
|
+
* propagate out of a pi session. The flow:
|
|
89
|
+
*
|
|
90
|
+
* 1. Validate `event`/`event.model` — a malformed event returns cleanly.
|
|
91
|
+
* 2. Build the injected `lookup` over `ctx.modelRegistry.find`, wrapped in a
|
|
92
|
+
* `try/catch` that returns `undefined`. If `modelRegistry` is absent,
|
|
93
|
+
* degrade to an always-`undefined` lookup (warn-only).
|
|
94
|
+
* 3. `classifyModelSwitch(model, source, lookup)` → a `GuardAction`.
|
|
95
|
+
* 4. Execute the action: `ignore` → no-op; `warn` → `notify(..., "warning")`;
|
|
96
|
+
* `offer-redirect` → `confirm`, and on a truthy answer `setModel` +
|
|
97
|
+
* `notify(..., "info")` (or the failure notice if `setModel` returns
|
|
98
|
+
* `false`). A falsy `confirm` leaves the user on the metered provider.
|
|
99
|
+
*/
|
|
100
|
+
async function handleModelSelect(
|
|
101
|
+
event: PiModelSelectEvent,
|
|
102
|
+
ctx: PiModelSelectContext,
|
|
103
|
+
pi: PiExtensionAPI,
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
try {
|
|
106
|
+
// 1. Malformed event — no event, no model, or no UI to talk to: bail out.
|
|
107
|
+
if (!event || !event.model || !ctx || !ctx.ui || typeof ctx.ui.notify !== "function") {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 2. Build the injected lookup. `classifyModelSwitch` assumes a total
|
|
112
|
+
// lookup, so `find` is wrapped in a `try/catch` returning `undefined`.
|
|
113
|
+
// If `modelRegistry` is absent the guard cannot resolve equivalents —
|
|
114
|
+
// degrade to an always-`undefined` lookup so a metered provider still
|
|
115
|
+
// produces a warning rather than breaking.
|
|
116
|
+
const registry = ctx.modelRegistry;
|
|
117
|
+
const lookup = (provider: string, id: string): ModelLike | undefined => {
|
|
118
|
+
if (!registry || typeof registry.find !== "function") return undefined;
|
|
119
|
+
try {
|
|
120
|
+
return registry.find(provider, id);
|
|
121
|
+
} catch {
|
|
122
|
+
// A registry lookup failure degrades to "no equivalent", never throws.
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// 3. Classify the switch in the pure module.
|
|
128
|
+
const action = classifyModelSwitch(event.model, event.source, lookup);
|
|
129
|
+
|
|
130
|
+
// 4. Execute the action.
|
|
131
|
+
if (action.kind === "ignore") {
|
|
132
|
+
// Non-metered provider or malformed event — nothing to do.
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (action.kind === "warn") {
|
|
137
|
+
// Metered provider with no equivalent, or a session `restore` — a single
|
|
138
|
+
// non-blocking warning, no modal, no `setModel`.
|
|
139
|
+
ctx.ui.notify(action.message, "warning");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// action.kind === "offer-redirect" — deliberate switch to a metered
|
|
144
|
+
// provider that has a subscription equivalent. Offer the redirect; the
|
|
145
|
+
// `confirm` dialog itself is the user's escape (AC 1.5).
|
|
146
|
+
const accepted = await ctx.ui.confirm(action.confirmTitle, action.confirmMessage);
|
|
147
|
+
if (!accepted) {
|
|
148
|
+
// User declined or cancelled — leave them on the metered provider, no
|
|
149
|
+
// second warning, no `setModel`.
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const applied = await pi.setModel(action.safeModel);
|
|
154
|
+
if (applied) {
|
|
155
|
+
// Redirection took effect — confirm it with an `info` notification.
|
|
156
|
+
ctx.ui.notify(action.redirectMessage, "info");
|
|
157
|
+
} else {
|
|
158
|
+
// `setModel` returned `false`: the switch did not apply. Do not claim
|
|
159
|
+
// "redirected" — emit the optional failure notice instead.
|
|
160
|
+
ctx.ui.notify(
|
|
161
|
+
redirectFailedMessage(action.safeModel.id, action.safeModel.provider),
|
|
162
|
+
"warning",
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// Any failure of the guard must never break a pi session.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Registration
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The pi extension entry point.
|
|
176
|
+
*
|
|
177
|
+
* pi calls this once when the extension loads. It wires the guard handler to
|
|
178
|
+
* the `model_select` event. The whole body is wrapped in a swallowing
|
|
179
|
+
* `try/catch`, and the handler is wrapped again, so neither registration nor a
|
|
180
|
+
* later failure can ever break a pi session. Called with no or an invalid
|
|
181
|
+
* `pi` (missing, or no `.on` function), it no-ops cleanly.
|
|
182
|
+
*/
|
|
183
|
+
export default function register(pi?: unknown): void {
|
|
184
|
+
try {
|
|
185
|
+
if (!pi || typeof (pi as PiExtensionAPI).on !== "function") {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const api = pi as PiExtensionAPI;
|
|
189
|
+
api.on("model_select", (event: PiModelSelectEvent, ctx: PiModelSelectContext): Promise<void> => {
|
|
190
|
+
return handleModelSelect(event, ctx, api);
|
|
191
|
+
});
|
|
192
|
+
} catch {
|
|
193
|
+
// Registration itself must never break a pi session.
|
|
194
|
+
}
|
|
195
|
+
}
|