@melihmucuk/pi-crew 1.0.17 → 1.0.18

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 (31) hide show
  1. package/extension/catalog.ts +543 -0
  2. package/extension/crew.ts +383 -0
  3. package/extension/index.ts +5 -6
  4. package/extension/subagent-session.ts +270 -0
  5. package/extension/tools.ts +323 -0
  6. package/extension/ui.ts +309 -0
  7. package/package.json +1 -1
  8. package/extension/agent-catalog.ts +0 -369
  9. package/extension/agent-config-fields.ts +0 -359
  10. package/extension/agent-discovery.ts +0 -123
  11. package/extension/bootstrap-session.ts +0 -131
  12. package/extension/integration/crew-tool-actions.ts +0 -306
  13. package/extension/integration/crew-tool-executor.ts +0 -109
  14. package/extension/integration/register-renderers.ts +0 -77
  15. package/extension/integration/register-tools.ts +0 -47
  16. package/extension/integration/tool-presentation.ts +0 -30
  17. package/extension/integration/tools/crew-abort.ts +0 -56
  18. package/extension/integration/tools/crew-done.ts +0 -27
  19. package/extension/integration/tools/crew-list.ts +0 -36
  20. package/extension/integration/tools/crew-respond.ts +0 -38
  21. package/extension/integration/tools/crew-spawn.ts +0 -46
  22. package/extension/message-delivery-policy.ts +0 -22
  23. package/extension/runtime/crew-runtime.ts +0 -263
  24. package/extension/runtime/owner-session-coordinator.ts +0 -138
  25. package/extension/runtime/subagent-lifecycle.ts +0 -203
  26. package/extension/runtime/subagent-registry.ts +0 -122
  27. package/extension/runtime/subagent-transitions.ts +0 -100
  28. package/extension/status-widget.ts +0 -107
  29. package/extension/subagent-messages.ts +0 -116
  30. package/extension/tool-registry.ts +0 -19
  31. /package/extension/{runtime/overflow-recovery.ts → overflow-recovery.ts} +0 -0
@@ -1,138 +0,0 @@
1
- import type { SendMessageFn } from "../message-delivery-policy.js";
2
- import {
3
- type SteeringPayload,
4
- sendRemainingNote,
5
- sendSteeringMessage,
6
- } from "../subagent-messages.js";
7
-
8
- export interface ActiveRuntimeBinding {
9
- sessionId: string;
10
- isIdle: () => boolean;
11
- sendMessage: SendMessageFn;
12
- }
13
-
14
- interface PendingMessage {
15
- ownerSessionId: string;
16
- payload: SteeringPayload;
17
- queuedAt: number;
18
- }
19
-
20
- interface OwnerSessionCoordinatorDeps {
21
- countRunningForOwner: (ownerSessionId: string, excludeId: string) => number;
22
- onRefreshOwnerSession: (ownerSessionId: string) => void;
23
- now?: () => number;
24
- scheduleFlush?: (callback: () => void) => void;
25
- }
26
-
27
- const PENDING_MESSAGE_TTL_MS = 86_400_000;
28
-
29
- export class OwnerSessionCoordinator {
30
- private binding: ActiveRuntimeBinding | undefined;
31
- private pendingMessages: PendingMessage[] = [];
32
- private flushScheduled = false;
33
- private readonly countRunningForOwner: (ownerSessionId: string, excludeId: string) => number;
34
- private readonly onRefreshOwnerSession: (ownerSessionId: string) => void;
35
- private readonly now: () => number;
36
- private readonly scheduleFlush: (callback: () => void) => void;
37
-
38
- constructor(deps: OwnerSessionCoordinatorDeps) {
39
- this.countRunningForOwner = deps.countRunningForOwner;
40
- this.onRefreshOwnerSession = deps.onRefreshOwnerSession;
41
- this.now = deps.now ?? Date.now;
42
- this.scheduleFlush = deps.scheduleFlush ?? ((callback) => setTimeout(callback, 0));
43
- }
44
-
45
- activateSession(binding: ActiveRuntimeBinding): void {
46
- this.binding = binding;
47
-
48
- // Delay flush to next macrotask. session_start fires before pi-core
49
- // calls _reconnectToAgent(), so synchronous delivery would emit agent
50
- // events while the session listener is disconnected, losing JSONL persistence.
51
- if (this.pendingMessages.some((entry) => entry.ownerSessionId === binding.sessionId)) {
52
- this.flushScheduled = true;
53
- this.scheduleFlush(() => {
54
- this.flushScheduled = false;
55
- this.flushPending();
56
- });
57
- }
58
- }
59
-
60
- deactivateSession(sessionId: string): void {
61
- if (this.binding?.sessionId === sessionId) {
62
- this.binding = undefined;
63
- }
64
- }
65
-
66
- refresh(ownerSessionId: string): void {
67
- this.onRefreshOwnerSession(ownerSessionId);
68
- }
69
-
70
- deliver(ownerSessionId: string, payload: SteeringPayload): void {
71
- if (!this.binding || ownerSessionId !== this.binding.sessionId || this.flushScheduled) {
72
- this.queue(ownerSessionId, payload);
73
- return;
74
- }
75
-
76
- this.send(ownerSessionId, payload);
77
- }
78
-
79
- private queue(ownerSessionId: string, payload: SteeringPayload): void {
80
- this.pendingMessages.push({ ownerSessionId, payload, queuedAt: this.now() });
81
- }
82
-
83
- private cleanStaleMessages(): void {
84
- const cutoff = this.now() - PENDING_MESSAGE_TTL_MS;
85
- this.pendingMessages = this.pendingMessages.filter(
86
- (entry) => entry.queuedAt >= cutoff,
87
- );
88
- }
89
-
90
- private flushPending(): void {
91
- if (!this.binding) return;
92
- const targetSessionId = this.binding.sessionId;
93
-
94
- this.cleanStaleMessages();
95
-
96
- const toDeliver: PendingMessage[] = [];
97
- const remaining: PendingMessage[] = [];
98
-
99
- for (const entry of this.pendingMessages) {
100
- if (entry.ownerSessionId === targetSessionId) {
101
- toDeliver.push(entry);
102
- } else {
103
- remaining.push(entry);
104
- }
105
- }
106
-
107
- this.pendingMessages = remaining;
108
-
109
- for (const entry of toDeliver) {
110
- this.send(entry.ownerSessionId, entry.payload);
111
- }
112
- }
113
-
114
- /**
115
- * Result messages always go first. If more subagents are still running and the
116
- * owner is idle, queue the result without triggering, then queue the separate
117
- * remaining note with triggerTurn so the next turn sees both in order.
118
- */
119
- private send(ownerSessionId: string, payload: SteeringPayload): void {
120
- if (!this.binding || this.binding.sessionId !== ownerSessionId) {
121
- this.queue(ownerSessionId, payload);
122
- return;
123
- }
124
-
125
- const remaining = this.countRunningForOwner(ownerSessionId, payload.id);
126
- const isIdle = this.binding.isIdle();
127
- const triggerResultTurn = !(isIdle && remaining > 0);
128
-
129
- sendSteeringMessage(payload, this.binding.sendMessage, {
130
- isIdle,
131
- triggerTurn: triggerResultTurn,
132
- });
133
- sendRemainingNote(remaining, this.binding.sendMessage, {
134
- isIdle,
135
- triggerTurn: isIdle && remaining > 0,
136
- });
137
- }
138
- }
@@ -1,203 +0,0 @@
1
- import type { AgentMessage } from "@earendil-works/pi-agent-core";
2
- import type { AssistantMessage } from "@earendil-works/pi-ai";
3
- import type { AgentSession } from "@earendil-works/pi-coding-agent";
4
- import type { BootstrapContext } from "../bootstrap-session.js";
5
- import { bootstrapSession } from "../bootstrap-session.js";
6
- import type { SubagentStatus } from "../subagent-messages.js";
7
- import { runPromptWithOverflowRecovery } from "./overflow-recovery.js";
8
- import type { SubagentState } from "./subagent-registry.js";
9
- import { isAborted } from "./subagent-transitions.js";
10
-
11
- interface PromptOutcome {
12
- status: Extract<SubagentStatus, "done" | "waiting" | "error" | "aborted">;
13
- result?: string;
14
- error?: string;
15
- }
16
-
17
- interface StartOptions {
18
- cwd: string;
19
- ctx: BootstrapContext;
20
- extensionResolvedPath: string;
21
- onWarning?: (message: string) => void;
22
- }
23
-
24
- interface SubagentLifecycleCallbacks {
25
- isCurrent: (state: SubagentState) => boolean;
26
- onProgress: (ownerSessionId: string) => void;
27
- onSettled: (
28
- state: SubagentState,
29
- status: Extract<SubagentStatus, "done" | "waiting" | "error" | "aborted">,
30
- outcome: { result?: string; error?: string },
31
- ) => void;
32
- }
33
-
34
- function getLastAssistantMessage(
35
- messages: AgentMessage[],
36
- ): AssistantMessage | undefined {
37
- for (let i = messages.length - 1; i >= 0; i--) {
38
- const msg = messages[i];
39
- if (msg.role === "assistant") {
40
- return msg as AssistantMessage;
41
- }
42
- }
43
- return undefined;
44
- }
45
-
46
- function getAssistantText(
47
- message: AssistantMessage | undefined,
48
- ): string | undefined {
49
- if (!message) return undefined;
50
-
51
- const texts: string[] = [];
52
- for (const part of message.content) {
53
- if (part.type === "text") {
54
- texts.push(part.text);
55
- }
56
- }
57
-
58
- return texts.length > 0 ? texts.join("\n") : undefined;
59
- }
60
-
61
- function getPromptOutcome(state: SubagentState): PromptOutcome {
62
- const lastAssistant = getLastAssistantMessage(state.session!.messages);
63
- const text = getAssistantText(lastAssistant);
64
-
65
- if (lastAssistant?.stopReason === "error") {
66
- return {
67
- status: "error",
68
- error: lastAssistant.errorMessage ?? text ?? "(no output)",
69
- };
70
- }
71
-
72
- if (lastAssistant?.stopReason === "aborted") {
73
- return {
74
- status: "aborted",
75
- error: lastAssistant.errorMessage ?? text ?? "(no output)",
76
- };
77
- }
78
-
79
- return {
80
- status: state.agentConfig.interactive ? "waiting" : "done",
81
- result: text ?? "(no output)",
82
- };
83
- }
84
-
85
- export class SubagentLifecycle {
86
- constructor(private readonly callbacks: SubagentLifecycleCallbacks) {}
87
-
88
- start(state: SubagentState, opts: StartOptions): void {
89
- void this.spawnSession(state, opts);
90
- }
91
-
92
- respond(state: SubagentState, message: string): void {
93
- void this.runPromptCycle(state, message);
94
- }
95
-
96
- abortPrompt(state: SubagentState): void {
97
- state.promptAbortController?.abort();
98
- state.promptAbortController = undefined;
99
- state.session?.abortCompaction();
100
- state.session?.abortRetry();
101
- state.session?.abort().catch(() => {});
102
- }
103
-
104
- private attachSessionListeners(
105
- state: SubagentState,
106
- session: AgentSession,
107
- ): void {
108
- state.unsubscribe = session.subscribe((event) => {
109
- if (event.type !== "turn_end") return;
110
-
111
- state.turns++;
112
- const msg = event.message;
113
- if (msg.role === "assistant") {
114
- const assistantMsg = msg as AssistantMessage;
115
- state.contextTokens = assistantMsg.usage.totalTokens;
116
- state.model = assistantMsg.model;
117
- }
118
- this.callbacks.onProgress(state.ownerSessionId);
119
- });
120
- }
121
-
122
- private attachSpawnedSession(
123
- state: SubagentState,
124
- session: AgentSession,
125
- ): boolean {
126
- if (!this.callbacks.isCurrent(state)) {
127
- session.dispose();
128
- return false;
129
- }
130
-
131
- state.session = session;
132
- return true;
133
- }
134
-
135
- private async runPromptCycle(
136
- state: SubagentState,
137
- prompt: string,
138
- ): Promise<void> {
139
- if (isAborted(state)) return;
140
-
141
- const abortController = new AbortController();
142
- state.promptAbortController = abortController;
143
-
144
- try {
145
- const recovery = await runPromptWithOverflowRecovery(
146
- state.session!,
147
- prompt,
148
- abortController.signal,
149
- );
150
- if (isAborted(state)) return;
151
-
152
- const outcome = getPromptOutcome(state);
153
-
154
- if (recovery === "failed" && outcome.status !== "error") {
155
- this.callbacks.onSettled(state, "error", {
156
- error: "Context overflow recovery failed",
157
- });
158
- return;
159
- }
160
-
161
- this.callbacks.onSettled(state, outcome.status, outcome);
162
- } catch (err) {
163
- if (isAborted(state)) return;
164
-
165
- const error = err instanceof Error ? err.message : String(err);
166
- this.callbacks.onSettled(state, "error", { error });
167
- } finally {
168
- state.promptAbortController = undefined;
169
- }
170
- }
171
-
172
- private async spawnSession(
173
- state: SubagentState,
174
- opts: StartOptions,
175
- ): Promise<void> {
176
- try {
177
- if (isAborted(state)) return;
178
-
179
- const { session, warnings } = await bootstrapSession({
180
- agentConfig: state.agentConfig,
181
- cwd: opts.cwd,
182
- ctx: opts.ctx,
183
- extensionResolvedPath: opts.extensionResolvedPath,
184
- });
185
-
186
- for (const warning of warnings) {
187
- opts.onWarning?.(warning);
188
- }
189
-
190
- if (!this.attachSpawnedSession(state, session)) return;
191
-
192
- this.attachSessionListeners(state, session);
193
- await this.runPromptCycle(state, state.task);
194
- } catch (err) {
195
- if (isAborted(state)) return;
196
-
197
- if (state.status === "running") {
198
- const error = err instanceof Error ? err.message : String(err);
199
- this.callbacks.onSettled(state, "error", { error });
200
- }
201
- }
202
- }
203
- }
@@ -1,122 +0,0 @@
1
- import { randomBytes } from "node:crypto";
2
- import type { AgentSession } from "@earendil-works/pi-coding-agent";
3
- import type { AgentConfig } from "../agent-discovery.js";
4
- import type { SubagentStatus } from "../subagent-messages.js";
5
- import { isAbortableStatus } from "./subagent-transitions.js";
6
-
7
- export interface SubagentState {
8
- id: string;
9
- agentConfig: AgentConfig;
10
- task: string;
11
- status: SubagentStatus;
12
- ownerSessionId: string;
13
- session: AgentSession | null;
14
- turns: number;
15
- contextTokens: number;
16
- model: string | undefined;
17
- error?: string;
18
- result?: string;
19
- promptAbortController?: AbortController;
20
- unsubscribe?: () => void;
21
- }
22
-
23
- export interface ActiveAgentSummary {
24
- id: string;
25
- agentName: string;
26
- status: SubagentStatus;
27
- turns: number;
28
- contextTokens: number;
29
- model: string | undefined;
30
- }
31
-
32
- function generateId(name: string, existingIds: Set<string>): string {
33
- for (let i = 0; i < 10; i++) {
34
- const id = `${name}-${randomBytes(4).toString("hex")}`;
35
- if (!existingIds.has(id)) return id;
36
- }
37
- return `${name}-${randomBytes(8).toString("hex")}`;
38
- }
39
-
40
- function buildActiveAgentSummary(
41
- state: SubagentState,
42
- ): ActiveAgentSummary {
43
- return {
44
- id: state.id,
45
- agentName: state.agentConfig.name,
46
- status: state.status,
47
- turns: state.turns,
48
- contextTokens: state.contextTokens,
49
- model: state.model,
50
- };
51
- }
52
-
53
- export class SubagentRegistry {
54
- private activeAgents = new Map<string, SubagentState>();
55
-
56
- create(agentConfig: AgentConfig, task: string, ownerSessionId: string): SubagentState {
57
- const id = generateId(agentConfig.name, new Set(this.activeAgents.keys()));
58
- const state: SubagentState = {
59
- id,
60
- agentConfig,
61
- task,
62
- status: "running",
63
- ownerSessionId,
64
- session: null,
65
- turns: 0,
66
- contextTokens: 0,
67
- model: undefined,
68
- };
69
-
70
- this.activeAgents.set(id, state);
71
- return state;
72
- }
73
-
74
- get(id: string): SubagentState | undefined {
75
- return this.activeAgents.get(id);
76
- }
77
-
78
- hasState(state: SubagentState): boolean {
79
- return this.activeAgents.get(state.id) === state;
80
- }
81
-
82
- delete(id: string): void {
83
- this.activeAgents.delete(id);
84
- }
85
-
86
- countRunningForOwner(ownerSessionId: string, excludeId: string): number {
87
- let count = 0;
88
- for (const state of this.activeAgents.values()) {
89
- if (
90
- state.id !== excludeId &&
91
- state.ownerSessionId === ownerSessionId &&
92
- state.status === "running"
93
- ) {
94
- count++;
95
- }
96
- }
97
- return count;
98
- }
99
-
100
- getActiveSummariesForOwner(ownerSessionId: string): ActiveAgentSummary[] {
101
- return Array.from(this.activeAgents.values())
102
- .filter(
103
- (state) => isAbortableStatus(state.status) && state.ownerSessionId === ownerSessionId,
104
- )
105
- .map(buildActiveAgentSummary);
106
- }
107
-
108
- getOwnedAbortableIds(ownerSessionId: string): string[] {
109
- return Array.from(this.activeAgents.values())
110
- .filter(
111
- (state) =>
112
- state.ownerSessionId === ownerSessionId && isAbortableStatus(state.status),
113
- )
114
- .map((state) => state.id);
115
- }
116
-
117
- getAllAbortable(): SubagentState[] {
118
- return Array.from(this.activeAgents.values()).filter((state) =>
119
- isAbortableStatus(state.status),
120
- );
121
- }
122
- }
@@ -1,100 +0,0 @@
1
- import type { SubagentStatus } from "../subagent-messages.js";
2
- import type { SubagentState } from "./subagent-registry.js";
3
-
4
- export type SettledSubagentStatus = Extract<
5
- SubagentStatus,
6
- "done" | "waiting" | "error" | "aborted"
7
- >;
8
-
9
- export interface SubagentTransitionOutcome {
10
- result?: string;
11
- error?: string;
12
- }
13
-
14
- type SubagentTransitionResult =
15
- | { ok: true; state: SubagentState }
16
- | { ok: false; error: string };
17
-
18
- export function isAborted(state: SubagentState): boolean {
19
- return state.status === "aborted";
20
- }
21
-
22
- export function isAbortableStatus(status: SubagentStatus): boolean {
23
- return status === "running" || status === "waiting";
24
- }
25
-
26
- export function canAbortSubagent(
27
- state: SubagentState | undefined,
28
- ): state is SubagentState {
29
- return Boolean(state && isAbortableStatus(state.status));
30
- }
31
-
32
- function validateOwnedSubagent(
33
- state: SubagentState | undefined,
34
- id: string,
35
- callerSessionId: string,
36
- missingMessage: string,
37
- ): SubagentTransitionResult {
38
- if (!state) return { ok: false, error: missingMessage };
39
- if (state.ownerSessionId !== callerSessionId) {
40
- return { ok: false, error: `Subagent "${id}" belongs to a different session` };
41
- }
42
- return { ok: true, state };
43
- }
44
-
45
- export function startSubagentResponse(
46
- state: SubagentState | undefined,
47
- id: string,
48
- callerSessionId: string,
49
- ): SubagentTransitionResult {
50
- const owned = validateOwnedSubagent(
51
- state,
52
- id,
53
- callerSessionId,
54
- `No subagent with id "${id}"`,
55
- );
56
- if (!owned.ok) return owned;
57
-
58
- if (owned.state.status !== "waiting") {
59
- return {
60
- ok: false,
61
- error: `Subagent "${id}" is not waiting for a response (status: ${owned.state.status})`,
62
- };
63
- }
64
- if (!owned.state.session) {
65
- return { ok: false, error: `Subagent "${id}" has no active session` };
66
- }
67
-
68
- owned.state.status = "running";
69
- return owned;
70
- }
71
-
72
- export function validateSubagentDone(
73
- state: SubagentState | undefined,
74
- id: string,
75
- callerSessionId: string,
76
- ): SubagentTransitionResult {
77
- const owned = validateOwnedSubagent(
78
- state,
79
- id,
80
- callerSessionId,
81
- `No active subagent with id "${id}"`,
82
- );
83
- if (!owned.ok) return owned;
84
-
85
- if (owned.state.status !== "waiting") {
86
- return { ok: false, error: `Subagent "${id}" is not in waiting state` };
87
- }
88
-
89
- return owned;
90
- }
91
-
92
- export function settleSubagent(
93
- state: SubagentState,
94
- status: SettledSubagentStatus,
95
- outcome: SubagentTransitionOutcome,
96
- ): void {
97
- state.status = status;
98
- state.result = outcome.result;
99
- state.error = outcome.error;
100
- }
@@ -1,107 +0,0 @@
1
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { Text } from "@earendil-works/pi-tui";
3
- import type { ActiveAgentSummary } from "./runtime/crew-runtime.js";
4
- import type { CrewRuntime } from "./runtime/crew-runtime.js";
5
-
6
- const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
7
- const SPINNER_INTERVAL_MS = 80;
8
-
9
- function formatTokens(tokens: number): string {
10
- if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;
11
- if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(1)}k`;
12
- return String(tokens);
13
- }
14
-
15
- function buildLine(agent: ActiveAgentSummary, frame: string): string {
16
- const model = agent.model ?? "…";
17
- const icon = agent.status === "waiting" ? "⏳" : frame;
18
- return `${icon} ${agent.id} (${model}) · turn ${agent.turns} · ${formatTokens(agent.contextTokens)} ctx`;
19
- }
20
-
21
- interface WidgetState {
22
- ctx: ExtensionContext;
23
- text: Text;
24
- // biome-ignore lint: TUI type from factory param
25
- tui: any;
26
- timer: ReturnType<typeof setInterval>;
27
- frameIndex: number;
28
- }
29
-
30
- let widget: WidgetState | undefined;
31
-
32
- function disposeWidget(state: WidgetState): void {
33
- clearInterval(state.timer);
34
- if (widget === state) {
35
- widget = undefined;
36
- }
37
- }
38
-
39
- function clearWidget(): void {
40
- const current = widget;
41
- if (!current) return;
42
- disposeWidget(current);
43
- current.ctx.ui.setWidget("crew-status", undefined);
44
- }
45
-
46
- function hasRunningAgent(agents: ActiveAgentSummary[]): boolean {
47
- return agents.some((agent) => agent.status === "running");
48
- }
49
-
50
- function syncWidgetText(state: WidgetState, agents: ActiveAgentSummary[]): void {
51
- const frame = SPINNER_FRAMES[state.frameIndex % SPINNER_FRAMES.length];
52
- const lines = agents.map((agent) => buildLine(agent, frame));
53
- state.text.setText(lines.join("\n"));
54
- state.tui.requestRender();
55
- }
56
-
57
- export function updateWidget(ctx: ExtensionContext, crew: CrewRuntime): void {
58
- if (!ctx.hasUI) {
59
- clearWidget();
60
- return;
61
- }
62
-
63
- const ownerSessionId = ctx.sessionManager.getSessionId();
64
- const running = crew.getActiveSummariesForOwner(ownerSessionId);
65
- if (running.length === 0) {
66
- clearWidget();
67
- return;
68
- }
69
-
70
- if (widget && widget.ctx !== ctx) {
71
- clearWidget();
72
- }
73
-
74
- if (widget) {
75
- syncWidgetText(widget, running);
76
- return;
77
- }
78
-
79
- ctx.ui.setWidget("crew-status", (tui, _theme) => {
80
- const text = new Text("", 1, 0);
81
- const state: WidgetState = {
82
- ctx,
83
- text,
84
- tui,
85
- frameIndex: 0,
86
- timer: setInterval(() => {
87
- const agents = crew.getActiveSummariesForOwner(ownerSessionId);
88
- if (agents.length === 0) {
89
- clearWidget();
90
- return;
91
- }
92
- if (!hasRunningAgent(agents)) return;
93
- state.frameIndex++;
94
- syncWidgetText(state, agents);
95
- }, SPINNER_INTERVAL_MS),
96
- };
97
-
98
- widget = state;
99
- syncWidgetText(state, running);
100
-
101
- return Object.assign(text, {
102
- dispose() {
103
- disposeWidget(state);
104
- },
105
- });
106
- });
107
- }