@evandu/super-pi 0.1.131 → 0.1.133

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 (70) hide show
  1. package/README.md +18 -0
  2. package/agents/super-pi-feishu/index.ts +88 -0
  3. package/agents/super-pi-knowledge/index.ts +1 -1
  4. package/agents/super-pi-task/core.ts +177 -0
  5. package/agents/super-pi-task/index.ts +59 -0
  6. package/dist/.super-pi-check-inputs.json +2 -2
  7. package/dist/client/.super-pi-client-sources.json +12 -9
  8. package/dist/client/assets/CodeMirrorEditor-CZZfGD0U.js +2 -0
  9. package/dist/client/assets/{dist-B448EWTM.js → dist-0FH-oFco.js} +1 -1
  10. package/dist/client/assets/{dist-Fhkmvjdx.js → dist-4Pts9xPO.js} +1 -1
  11. package/dist/client/assets/{dist-BcE6tJvc.js → dist-B5ruVtUG.js} +1 -1
  12. package/dist/client/assets/{dist-CpuexUlh.js → dist-BJinarhP.js} +1 -1
  13. package/dist/client/assets/{dist-Cybsoapc.js → dist-BffE00vI.js} +1 -1
  14. package/dist/client/assets/{dist-DxoWS5MH.js → dist-BtTFCNVQ.js} +1 -1
  15. package/dist/client/assets/{dist-CtdlYgGc.js → dist-C2nuBCP4.js} +1 -1
  16. package/dist/client/assets/{dist-hfpd-aWa.js → dist-CApAttfj.js} +1 -1
  17. package/dist/client/assets/{dist-D6PoSxR5.js → dist-CE5BnwJa.js} +1 -1
  18. package/dist/client/assets/{dist-COp25nhe.js → dist-CFPXj4ws.js} +1 -1
  19. package/dist/client/assets/{dist-D7bj81KQ.js → dist-CUKaDTyO.js} +1 -1
  20. package/dist/client/assets/{dist-BFlq-kiF.js → dist-CYD2SqmG.js} +1 -1
  21. package/dist/client/assets/{dist-lQWWWxh9.js → dist-CxsunljU.js} +1 -1
  22. package/dist/client/assets/{dist-BXVFgriA.js → dist-D4x1XTQb.js} +1 -1
  23. package/dist/client/assets/{dist-DXNDtyHP.js → dist-DHqqpxo4.js} +1 -1
  24. package/dist/client/assets/{dist-r5Af94tH.js → dist-DOYWHMIq.js} +1 -1
  25. package/dist/client/assets/{dist-B68Q8uXX.js → dist-DQf5nNxa.js} +1 -1
  26. package/dist/client/assets/{dist-C6qSkkeN.js → dist-DbbBgSwL.js} +1 -1
  27. package/dist/client/assets/{dist-B1_1Ui9E.js → dist-LFfokYJs.js} +1 -1
  28. package/dist/client/assets/{dist-ByIqsb5-.js → dist-aL2NGji_.js} +1 -1
  29. package/dist/client/assets/{dist-C9AkgNbt.js → dist-azDN8a65.js} +1 -1
  30. package/dist/client/assets/{dist-DP2ygAFF.js → dist-fYp8Xwrx.js} +1 -1
  31. package/dist/client/assets/{index-CXB6U5mz.css → index-D3MEG8uH.css} +1 -1
  32. package/dist/client/assets/index-YEN4ysHF.js +100 -0
  33. package/dist/client/index.html +2 -2
  34. package/dist/server/agents.js +21 -1
  35. package/dist/server/application/feishu-service.d.ts +179 -0
  36. package/dist/server/application/feishu-service.js +358 -0
  37. package/dist/server/application/run-service.js +15 -1
  38. package/dist/server/application/scheduled-task-service.d.ts +164 -0
  39. package/dist/server/application/scheduled-task-service.js +350 -0
  40. package/dist/server/command-handler.d.ts +25 -1
  41. package/dist/server/command-handler.js +60 -1
  42. package/dist/server/feishu-cli.d.ts +223 -0
  43. package/dist/server/feishu-cli.js +453 -0
  44. package/dist/server/feishu-config.d.ts +68 -0
  45. package/dist/server/feishu-config.js +84 -0
  46. package/dist/server/feishu.d.ts +187 -0
  47. package/dist/server/feishu.js +695 -0
  48. package/dist/server/files.js +3 -3
  49. package/dist/server/http-server.js +232 -1
  50. package/dist/server/index.js +29 -2
  51. package/dist/server/knowledge.js +10 -2
  52. package/dist/server/persistence/app-state-repository.d.ts +13 -1
  53. package/dist/server/persistence/sqlite-state-repository.js +21 -0
  54. package/dist/server/pi-session-events.js +10 -2
  55. package/dist/server/pi-session-pool.d.ts +33 -1
  56. package/dist/server/pi-session-pool.js +20 -2
  57. package/dist/server/pi-session.js +3 -0
  58. package/dist/server/runtime.d.ts +47 -1
  59. package/dist/server/runtime.js +110 -15
  60. package/dist/server/scheduled-tasks.d.ts +101 -0
  61. package/dist/server/scheduled-tasks.js +138 -0
  62. package/dist/server/server-context.d.ts +16 -0
  63. package/dist/server/store.d.ts +30 -0
  64. package/dist/server/store.js +81 -2
  65. package/dist/server/task-tool.d.ts +18 -0
  66. package/dist/server/task-tool.js +18 -0
  67. package/dist/server/types.d.ts +56 -0
  68. package/package.json +26 -4
  69. package/dist/client/assets/CodeMirrorEditor-BlQhH3vs.js +0 -2
  70. package/dist/client/assets/index-BApJ3Zbq.js +0 -98
@@ -1,5 +1,6 @@
1
1
  import type { AppStateRepository } from "./persistence/app-state-repository.js";
2
2
  import { defaultJsonStatePath } from "./persistence/json-state-repository.js";
3
+ import type { ScheduledTask } from "./scheduled-tasks.js";
3
4
  import type { WorkflowRun } from "./workflow.js";
4
5
  import type { AppState, Conversation, Message, MessageDelivery, MessageImage, ModelSelection, SubagentRun, SubagentRunUpdate, TodoItem, ToolCallQuestion, ToolCallArg, ToolCallBlock, ToolResultSummary, Workspace } from "./types.js";
5
6
  /** Mints the id a forked conversation will have, BEFORE its Pi transcript exists.
@@ -45,6 +46,14 @@ export declare const SUBAGENT_RUN_RETENTION = 100;
45
46
  * request that needed several specialists rather than a unit of work.
46
47
  */
47
48
  export declare const WORKFLOW_RETENTION = 50;
49
+ /**
50
+ * How many scheduled tasks are kept.
51
+ *
52
+ * A task is a standing arrangement, not a record of something that happened, so the bound here is much
53
+ * looser than the run retention: a person who keeps a daily task for a year has one task, and the
54
+ * hundredth is already a sign that the list is being used as a log rather than as a list of intentions.
55
+ */
56
+ export declare const SCHEDULED_TASK_RETENTION = 100;
48
57
  /**
49
58
  * Everything a revert decided, before anything is changed.
50
59
  *
@@ -688,6 +697,27 @@ export declare class Store {
688
697
  setBackgroundTask(summary: AppState["backgroundTasks"][number]): Promise<void>;
689
698
  removeBackgroundTask(id: string): Promise<void>;
690
699
  clearBackgroundTasks(): Promise<void>;
700
+ /**
701
+ * Writes a scheduled task - new or changed - and persists it.
702
+ *
703
+ * ONE METHOD FOR BOTH, because the two are the same write. A task that has not run and a task that
704
+ * has run are the same kind of thing with different field values, and splitting them into
705
+ * "create" and "update" would leave the caller to decide which of two paths a given change takes -
706
+ * which is exactly the kind of decision that ends up made twice, differently, in two places.
707
+ *
708
+ * The value is cloned on the way in and on the way out, like every other store method that hands a
709
+ * record back: the caller mutates its own copy freely, and the copy on disk is the copy that was
710
+ * written.
711
+ */
712
+ setScheduledTask(task: ScheduledTask): Promise<ScheduledTask>;
713
+ /**
714
+ * Removes a scheduled task, and persists the removal.
715
+ *
716
+ * AN EMPTY LIST IS STORED AS ABSENCE, for the same reason `setTodos` stores an empty todo list as
717
+ * the absence of the key: a state file that says "scheduledTasks: []" claims the user considered a
718
+ * schedule and found none, and a fresh install has never considered anything.
719
+ */
720
+ removeScheduledTask(id: string): Promise<void>;
691
721
  /**
692
722
  * Applies the fields a run's life can change, and nothing else.
693
723
  *
@@ -245,6 +245,27 @@ function trimWorkflows(workflows) {
245
245
  }
246
246
  workflows.splice(0, workflows.length, ...workflows.filter((plan) => !doomed.has(plan.id)));
247
247
  }
248
+ /**
249
+ * How many scheduled tasks are kept.
250
+ *
251
+ * A task is a standing arrangement, not a record of something that happened, so the bound here is much
252
+ * looser than the run retention: a person who keeps a daily task for a year has one task, and the
253
+ * hundredth is already a sign that the list is being used as a log rather than as a list of intentions.
254
+ */
255
+ export const SCHEDULED_TASK_RETENTION = 100;
256
+ /** Evicts the OLDEST scheduled tasks once there are more than `SCHEDULED_TASK_RETENTION`. */
257
+ function trimScheduledTasks(tasks) {
258
+ const excess = tasks.length - SCHEDULED_TASK_RETENTION;
259
+ if (excess <= 0)
260
+ return;
261
+ const doomed = new Set();
262
+ for (const task of [...tasks].sort((a, b) => a.createdAt - b.createdAt)) {
263
+ if (doomed.size >= excess)
264
+ break;
265
+ doomed.add(task.id);
266
+ }
267
+ tasks.splice(0, tasks.length, ...tasks.filter((task) => !doomed.has(task.id)));
268
+ }
248
269
  /**
249
270
  * Why a plan the previous process left `running` is stopped rather than resumed, in the same spirit and
250
271
  * for the same audience as `ORPHANED_RUN_ERROR`.
@@ -576,9 +597,15 @@ export class Store {
576
597
  // it is repaired again on every single boot - and until it is, anything reading the file sees the
577
598
  // un-normalised shape rather than the one the store is actually running on.
578
599
  const defaulted = ["workspaces", "conversations", "subagentRuns"].filter((key) => !Array.isArray(state[key])).length > 0
579
- || !Array.isArray(state.backgroundTasks);
600
+ || !Array.isArray(state.backgroundTasks)
601
+ || (state.scheduledTasks !== undefined && !Array.isArray(state.scheduledTasks));
580
602
  if (!Array.isArray(state.backgroundTasks))
581
603
  state.backgroundTasks = [];
604
+ // A scheduled-task list that is not a list is not a list of tasks. The key is dropped rather than
605
+ // coerced, so a corrupted file reads as "this install has no scheduled tasks" - the same thing a
606
+ // fresh install reads - instead of crashing the panel on every poll.
607
+ if (state.scheduledTasks !== undefined && !Array.isArray(state.scheduledTasks))
608
+ delete state.scheduledTasks;
582
609
  // Every sweep runs before the store is constructed, so a single persist below publishes them together.
583
610
  // Reconciling them in separate writes would put a file on disk in which one kind of orphan was fixed
584
611
  // and the other was not - a half-repaired state that still needs a second restart to finish.
@@ -1970,6 +1997,50 @@ export class Store {
1970
1997
  this.state.backgroundTasks = [];
1971
1998
  await this.persistCollections("backgroundTasks");
1972
1999
  }
2000
+ /**
2001
+ * Writes a scheduled task - new or changed - and persists it.
2002
+ *
2003
+ * ONE METHOD FOR BOTH, because the two are the same write. A task that has not run and a task that
2004
+ * has run are the same kind of thing with different field values, and splitting them into
2005
+ * "create" and "update" would leave the caller to decide which of two paths a given change takes -
2006
+ * which is exactly the kind of decision that ends up made twice, differently, in two places.
2007
+ *
2008
+ * The value is cloned on the way in and on the way out, like every other store method that hands a
2009
+ * record back: the caller mutates its own copy freely, and the copy on disk is the copy that was
2010
+ * written.
2011
+ */
2012
+ async setScheduledTask(task) {
2013
+ if (!this.state.scheduledTasks)
2014
+ this.state.scheduledTasks = [];
2015
+ const stored = structuredClone(task);
2016
+ const index = this.state.scheduledTasks.findIndex((item) => item?.id === stored.id);
2017
+ if (index < 0)
2018
+ this.state.scheduledTasks.push(stored);
2019
+ else
2020
+ this.state.scheduledTasks[index] = stored;
2021
+ trimScheduledTasks(this.state.scheduledTasks);
2022
+ await this.persistCollections("scheduledTasks");
2023
+ return structuredClone(stored);
2024
+ }
2025
+ /**
2026
+ * Removes a scheduled task, and persists the removal.
2027
+ *
2028
+ * AN EMPTY LIST IS STORED AS ABSENCE, for the same reason `setTodos` stores an empty todo list as
2029
+ * the absence of the key: a state file that says "scheduledTasks: []" claims the user considered a
2030
+ * schedule and found none, and a fresh install has never considered anything.
2031
+ */
2032
+ async removeScheduledTask(id) {
2033
+ if (!this.state.scheduledTasks)
2034
+ return;
2035
+ const remaining = this.state.scheduledTasks.filter((item) => item?.id !== id);
2036
+ if (remaining.length === this.state.scheduledTasks.length)
2037
+ return;
2038
+ if (remaining.length)
2039
+ this.state.scheduledTasks = remaining;
2040
+ else
2041
+ delete this.state.scheduledTasks;
2042
+ await this.persistCollections("scheduledTasks");
2043
+ }
1973
2044
  /**
1974
2045
  * Applies the fields a run's life can change, and nothing else.
1975
2046
  *
@@ -2063,7 +2134,15 @@ export class Store {
2063
2134
  await this.persist();
2064
2135
  }
2065
2136
  async persistCollections(key) {
2066
- await this.persistTransaction({ kind: "collections.replace", values: { [key]: structuredClone(this.state[key]) } });
2137
+ const value = this.state[key];
2138
+ // A collection that is EMPTY is absent from the state, and an absent collection has no row to
2139
+ // write. The row is removed instead, because a repository that can only write rows would have to
2140
+ // store `[]` - which is a different claim from "the user never made one".
2141
+ if (value === undefined) {
2142
+ await this.persistTransaction({ kind: "collections.remove", name: key });
2143
+ return;
2144
+ }
2145
+ await this.persistTransaction({ kind: "collections.replace", values: { [key]: structuredClone(value) } });
2067
2146
  }
2068
2147
  async persistMetadata(conversationId) {
2069
2148
  const { messages: _messages, ...metadata } = this.requireConversation(conversationId);
@@ -173,6 +173,24 @@ export declare const TODO_TOOL_NAME = "todowrite";
173
173
  * tool then refuses in words instead of posting somewhere that does not exist.
174
174
  */
175
175
  export declare const TODO_ENDPOINT_ENV = "SUPER_PI_TODO_ENDPOINT";
176
+ /**
177
+ * The name the model calls to manage the SCHEDULED TASKS.
178
+ *
179
+ * `scheduled_task`, with an underscore, because this is not one of OpenCode's tools and there is no
180
+ * prior name to match. The name says what it is: a task that runs on a schedule.
181
+ */
182
+ export declare const SCHEDULED_TASK_TOOL_NAME = "scheduled_task";
183
+ /**
184
+ * The endpoint the `scheduled_task` tool dials. Another name in the same wire, written out on both
185
+ * sides for the same reason `TODO_ENDPOINT_ENV` states: `agents/super-pi-task/core.ts` is loaded by
186
+ * Pi's module loader and this file by tsc, so the two copies have to agree by hand -
187
+ * `scripts/scheduled-task-tool.test.mjs` asserts that they do.
188
+ *
189
+ * A SEPARATE variable rather than a path derived from `TODO_ENDPOINT_ENV`, for the reason that one
190
+ * states: a Pi can legitimately have one and not the other, and a derived path could not tell those
191
+ * apart from a misconfiguration.
192
+ */
193
+ export declare const SCHEDULED_TASK_ENDPOINT_ENV = "SUPER_PI_SCHEDULED_TASK_ENDPOINT";
176
194
  /**
177
195
  * How many todos may be stored, and how long one may be.
178
196
  *
@@ -230,6 +230,24 @@ export const TODO_TOOL_NAME = "todowrite";
230
230
  * tool then refuses in words instead of posting somewhere that does not exist.
231
231
  */
232
232
  export const TODO_ENDPOINT_ENV = "SUPER_PI_TODO_ENDPOINT";
233
+ /**
234
+ * The name the model calls to manage the SCHEDULED TASKS.
235
+ *
236
+ * `scheduled_task`, with an underscore, because this is not one of OpenCode's tools and there is no
237
+ * prior name to match. The name says what it is: a task that runs on a schedule.
238
+ */
239
+ export const SCHEDULED_TASK_TOOL_NAME = "scheduled_task";
240
+ /**
241
+ * The endpoint the `scheduled_task` tool dials. Another name in the same wire, written out on both
242
+ * sides for the same reason `TODO_ENDPOINT_ENV` states: `agents/super-pi-task/core.ts` is loaded by
243
+ * Pi's module loader and this file by tsc, so the two copies have to agree by hand -
244
+ * `scripts/scheduled-task-tool.test.mjs` asserts that they do.
245
+ *
246
+ * A SEPARATE variable rather than a path derived from `TODO_ENDPOINT_ENV`, for the reason that one
247
+ * states: a Pi can legitimately have one and not the other, and a derived path could not tell those
248
+ * apart from a misconfiguration.
249
+ */
250
+ export const SCHEDULED_TASK_ENDPOINT_ENV = "SUPER_PI_SCHEDULED_TASK_ENDPOINT";
233
251
  /**
234
252
  * How many todos may be stored, and how long one may be.
235
253
  *
@@ -5,6 +5,7 @@ export type ModelFilterRule = {
5
5
  label?: string;
6
6
  };
7
7
  import type { WorkflowRun } from "./workflow.js";
8
+ import type { ScheduledTask } from "./scheduled-tasks.js";
8
9
  /**
9
10
  * The lifecycle states a `Conversation.status` moves through while a turn is in flight. `waiting` is the
10
11
  * gap between "prompt handed to the LLM" and "the first response token arrived" - holding it apart from
@@ -835,6 +836,7 @@ export type AppState = {
835
836
  backgroundTasks: BackgroundTaskSummary[];
836
837
  todosByConversation?: Record<string, TodoItem[]>;
837
838
  workflows?: WorkflowRun[];
839
+ scheduledTasks?: ScheduledTask[];
838
840
  customAgentDirs?: string[];
839
841
  };
840
842
  export type TodoItem = {
@@ -1372,6 +1374,60 @@ export type ClientCommandBody =
1372
1374
  /** Restarts the Super Pi server by spawning the same restart script used by the auto-updater. */
1373
1375
  | {
1374
1376
  type: "super_pi.restart";
1377
+ }
1378
+ /**
1379
+ * A scheduled task is created.
1380
+ *
1381
+ * The schedule arrives as an opaque value and is validated on the SERVER, in the one place that owns
1382
+ * what a schedule means. The browser cannot send a half-valid schedule, because it does not know
1383
+ * what a half-valid schedule is; it sends the fields the panel collected and the server decides
1384
+ * whether they describe a schedule.
1385
+ */
1386
+ | {
1387
+ type: "scheduledTask.create";
1388
+ name?: string;
1389
+ prompt: string;
1390
+ schedule: unknown;
1391
+ workspaceId: string;
1392
+ model?: ModelSelection | null;
1393
+ agentId?: string | null;
1394
+ }
1395
+ /**
1396
+ * A scheduled task is changed. Every field is optional, and the ones that are absent are the ones
1397
+ * that do not change - a panel that sends the whole task back would fight the scheduler's own
1398
+ * bookkeeping over `nextRunAt`, which is not the panel's to write.
1399
+ */
1400
+ | {
1401
+ type: "scheduledTask.update";
1402
+ id: string;
1403
+ name?: string;
1404
+ prompt?: string;
1405
+ schedule?: unknown;
1406
+ model?: ModelSelection | null;
1407
+ agentId?: string | null;
1408
+ }
1409
+ /** A scheduled task is removed. Its conversation and the answers it produced stay. */
1410
+ | {
1411
+ type: "scheduledTask.delete";
1412
+ id: string;
1413
+ }
1414
+ /**
1415
+ * A scheduled task runs NOW, once, without touching its schedule.
1416
+ *
1417
+ * It is a separate command rather than a flag on `update` because it is a different act: `update`
1418
+ * changes what the task IS, and this changes what the task DID. The one thing it shares with the
1419
+ * clock is the run itself - it goes through the same path, so a manual run and a scheduled run
1420
+ * behave identically.
1421
+ */
1422
+ | {
1423
+ type: "scheduledTask.trigger";
1424
+ id: string;
1425
+ }
1426
+ /** A scheduled task is turned on or off. */
1427
+ | {
1428
+ type: "scheduledTask.toggle";
1429
+ id: string;
1430
+ enabled: boolean;
1375
1431
  } | ({
1376
1432
  type: "extension.ui.answer";
1377
1433
  conversationId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.131",
3
+ "version": "0.1.133",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,9 +39,11 @@
39
39
  "start": "node dist/server/index.js",
40
40
  "restart:server": "node scripts/restart-server.mjs",
41
41
  "smoke": "npm run build && node scripts/smoke.mjs",
42
- "check": "node scripts/run-check.mjs"
42
+ "check": "node scripts/run-check.mjs",
43
+ "test": "node scripts/test.mjs"
43
44
  },
44
45
  "check:logic": [
46
+ "scripts/test-runner.test.mjs",
45
47
  "scripts/stream-checkpoint.test.mjs",
46
48
  "scripts/stream-transport.test.mjs",
47
49
  "scripts/inline-thinking.test.mjs",
@@ -86,6 +88,10 @@
86
88
  "scripts/followup.test.mjs",
87
89
  "scripts/pi-followup-queue.test.mjs",
88
90
  "scripts/background-result-delivery.test.mjs",
91
+ "scripts/thinking-status-promotion.test.mjs",
92
+ "scripts/question-call-session-path.test.mjs",
93
+ "scripts/withdrawn-message-persistence.test.mjs",
94
+ "scripts/git-write-endpoint-guard.test.mjs",
89
95
  "scripts/question-stop.test.mjs",
90
96
  "scripts/background-result-body.test.mjs",
91
97
  "scripts/question-request-size.test.mjs",
@@ -104,6 +110,7 @@
104
110
  "scripts/file-content-search.test.mjs",
105
111
  "scripts/file-diff-client.test.mjs",
106
112
  "scripts/editor-languages.test.mjs",
113
+ "scripts/editor-blame.test.mjs",
107
114
  "scripts/task-tool.test.mjs",
108
115
  "scripts/tool-register-env.test.mjs",
109
116
  "scripts/bg-task-zombie.test.mjs",
@@ -116,12 +123,16 @@
116
123
  "scripts/styles-no-dangling.test.mjs",
117
124
  "scripts/theme-no-hardcoded-colours.test.mjs",
118
125
  "scripts/agent-config-regression.test.mjs",
126
+ "scripts/agents-directory-resilience.test.mjs",
119
127
  "scripts/model-filter.test.mjs",
120
128
  "scripts/settings-default-model.test.mjs",
121
129
  "scripts/provider-registry.test.mjs",
122
130
  "scripts/provider-pi-reads.test.mjs",
131
+ "scripts/session-extension-credentials.test.mjs",
123
132
  "scripts/active-memory.test.mjs",
124
133
  "scripts/knowledge-base.test.mjs",
134
+ "scripts/knowledge-import-path.test.mjs",
135
+ "scripts/knowledge-import-identity.test.mjs",
125
136
  "scripts/tool-runner.test.mjs",
126
137
  "scripts/tool-runtime-api.test.mjs",
127
138
  "scripts/tool-sharing.test.mjs",
@@ -142,7 +153,11 @@
142
153
  "scripts/web-tools.test.mjs",
143
154
  "scripts/document-service.test.mjs",
144
155
  "scripts/document-block-parse.test.mjs",
145
- "scripts/document-write-overwrite.test.mjs"
156
+ "scripts/document-write-overwrite.test.mjs",
157
+ "scripts/feishu.test.mjs",
158
+ "scripts/scheduled-tasks.test.mjs",
159
+ "scripts/scheduled-task-scope.test.mjs",
160
+ "scripts/scheduled-task-tool.test.mjs"
146
161
  ],
147
162
  "check:browser": [
148
163
  "scripts/interrupt-browser.test.mjs",
@@ -159,13 +174,17 @@
159
174
  "scripts/session-state-animation-browser.test.mjs",
160
175
  "scripts/agent-vis-card-browser.test.mjs",
161
176
  "scripts/queue-dock.test.mjs",
177
+ "scripts/scheduled-tasks-browser.test.mjs",
162
178
  "scripts/md-render-browser.test.mjs",
163
179
  "scripts/right-sidebar-browser.test.mjs",
180
+ "scripts/rail-workspace-follows-session-browser.test.mjs",
164
181
  "scripts/sidebar-tools-browser.test.mjs",
165
182
  "scripts/tool-runtime-browser.test.mjs",
166
183
  "scripts/session-context-menu-browser.test.mjs",
167
184
  "scripts/tool-call-blocks-browser.test.mjs",
168
185
  "scripts/files-tab-browser.test.mjs",
186
+ "scripts/editor-blame-browser.test.mjs",
187
+ "scripts/file-panel-external-poll-browser.test.mjs",
169
188
  "scripts/file-quickopen-browser.test.mjs",
170
189
  "scripts/file-content-search-browser.test.mjs",
171
190
  "scripts/file-changes-browser.test.mjs",
@@ -188,12 +207,15 @@
188
207
  "scripts/composer-resize-browser.test.mjs",
189
208
  "scripts/workspace-picker-browser.test.mjs",
190
209
  "scripts/optimistic-browser.test.mjs",
210
+ "scripts/stop-while-waiting-browser.test.mjs",
211
+ "scripts/new-session-stays-selected-browser.test.mjs",
191
212
  "scripts/active-memory-browser.test.mjs",
192
213
  "scripts/knowledge-base-browser.test.mjs",
193
214
  "scripts/terminal-browser.test.mjs",
194
215
  "scripts/ux-polish-browser.test.mjs",
195
216
  "scripts/prompt-templates-browser.test.mjs",
196
- "scripts/documents-browser.test.mjs"
217
+ "scripts/documents-browser.test.mjs",
218
+ "scripts/feishu-browser.test.mjs"
197
219
  ],
198
220
  "dependencies": {
199
221
  "@codemirror/commands": "^6.11.1",
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/dist-DxoWS5MH.js","assets/dist-hfpd-aWa.js","assets/index-BApJ3Zbq.js","assets/index-CXB6U5mz.css","assets/dist-B68Q8uXX.js","assets/dist-CpuexUlh.js","assets/dist-D7bj81KQ.js","assets/dist-B1_1Ui9E.js","assets/dist-Cybsoapc.js","assets/dist-CtdlYgGc.js","assets/dist-ByIqsb5-.js","assets/dist-r5Af94tH.js","assets/dist-BXVFgriA.js","assets/dist-Fhkmvjdx.js","assets/dist-D6PoSxR5.js","assets/dist-lQWWWxh9.js","assets/dist-BFlq-kiF.js","assets/dist-DXNDtyHP.js","assets/dist-BcE6tJvc.js","assets/dist-B448EWTM.js","assets/dist-C6qSkkeN.js","assets/dist-COp25nhe.js","assets/dist-DP2ygAFF.js","assets/dist-C9AkgNbt.js"])))=>i.map(i=>d[i]);
2
- import{a as e,n as t,r as n,t as r}from"./index-BApJ3Zbq.js";var i=e(n(),1);async function a(e,t){let[{StreamLanguage:n},i]=await Promise.all([r(()=>import(`./dist-DxoWS5MH.js`),__vite__mapDeps([0,1,2,3,4,5])),e()]),a=i[t]??i.default??i;return n.define(a)}var o={cpp:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),cc:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),cxx:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),hpp:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),hh:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),hxx:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),c:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),h:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),cu:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),cuh:()=>r(()=>import(`./dist-D7bj81KQ.js`).then(e=>e.cpp()),__vite__mapDeps([6,5,2,3,0,1,4,7])),js:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript()),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),jsx:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript({jsx:!0})),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),mjs:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript()),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),cjs:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript()),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),ts:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript({typescript:!0})),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),tsx:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript({typescript:!0,jsx:!0})),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),mts:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript({typescript:!0})),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),cts:()=>r(()=>import(`./dist-Cybsoapc.js`).then(e=>e.t).then(e=>e.javascript({typescript:!0})),__vite__mapDeps([8,2,3,1,4,5,0,7,9])),py:()=>r(()=>import(`./dist-ByIqsb5-.js`).then(e=>e.python()),__vite__mapDeps([10,5,2,3,0,1,4,7,9])),pyi:()=>r(()=>import(`./dist-ByIqsb5-.js`).then(e=>e.python()),__vite__mapDeps([10,5,2,3,0,1,4,7,9])),java:()=>r(()=>import(`./dist-r5Af94tH.js`).then(e=>e.java()),__vite__mapDeps([11,5,2,3,0,1,4,7])),php:()=>r(()=>import(`./dist-BXVFgriA.js`).then(e=>e.php()),__vite__mapDeps([12,5,2,3,0,1,4,7,13,14,8,9])),sql:()=>r(()=>import(`./dist-lQWWWxh9.js`).then(e=>e.sql()),__vite__mapDeps([15,5,2,3,0,1,4,7,9])),yaml:()=>r(()=>import(`./dist-BFlq-kiF.js`).then(e=>e.yaml()),__vite__mapDeps([16,5,2,3,0,1,4,7])),yml:()=>r(()=>import(`./dist-BFlq-kiF.js`).then(e=>e.yaml()),__vite__mapDeps([16,5,2,3,0,1,4,7])),xml:()=>r(()=>import(`./dist-DXNDtyHP.js`).then(e=>e.xml()),__vite__mapDeps([17,1,2,3,4,5,0,7])),json:()=>r(()=>import(`./dist-BcE6tJvc.js`).then(e=>e.json()),__vite__mapDeps([18,5,2,3,0,1,4,7])),jsonc:()=>r(()=>import(`./dist-BcE6tJvc.js`).then(e=>e.json()),__vite__mapDeps([18,5,2,3,0,1,4,7])),json5:()=>r(()=>import(`./dist-BcE6tJvc.js`).then(e=>e.json()),__vite__mapDeps([18,5,2,3,0,1,4,7])),html:()=>r(()=>import(`./dist-Fhkmvjdx.js`).then(e=>e.html()),__vite__mapDeps([13,1,2,3,4,5,0,7,14,8,9])),htm:()=>r(()=>import(`./dist-Fhkmvjdx.js`).then(e=>e.html()),__vite__mapDeps([13,1,2,3,4,5,0,7,14,8,9])),vue:()=>r(()=>import(`./dist-Fhkmvjdx.js`).then(e=>e.html()),__vite__mapDeps([13,1,2,3,4,5,0,7,14,8,9])),svelte:()=>r(()=>import(`./dist-Fhkmvjdx.js`).then(e=>e.html()),__vite__mapDeps([13,1,2,3,4,5,0,7,14,8,9])),css:()=>r(()=>import(`./dist-D6PoSxR5.js`).then(e=>e.r).then(e=>e.css()),__vite__mapDeps([14,2,3,5,0,1,4,7])),md:()=>r(()=>import(`./dist-B448EWTM.js`).then(e=>e.markdown()),__vite__mapDeps([19,1,2,3,4,5,0,9,13,7,14,8])),markdown:()=>r(()=>import(`./dist-B448EWTM.js`).then(e=>e.markdown()),__vite__mapDeps([19,1,2,3,4,5,0,9,13,7,14,8])),mdx:()=>r(()=>import(`./dist-B448EWTM.js`).then(e=>e.markdown()),__vite__mapDeps([19,1,2,3,4,5,0,9,13,7,14,8])),rs:()=>r(()=>import(`./dist-C6qSkkeN.js`).then(e=>e.rust()),__vite__mapDeps([20,5,2,3,0,1,4,7])),go:()=>r(()=>import(`./dist-COp25nhe.js`).then(e=>e.go()),__vite__mapDeps([21,5,2,3,0,1,4,7,9])),sh:()=>a(()=>r(()=>import(`./shell-DwuoZtxw.js`),[]),`shell`),bash:()=>a(()=>r(()=>import(`./shell-DwuoZtxw.js`),[]),`shell`),zsh:()=>a(()=>r(()=>import(`./shell-DwuoZtxw.js`),[]),`shell`),fish:()=>a(()=>r(()=>import(`./shell-DwuoZtxw.js`),[]),`shell`),ps1:()=>a(()=>r(()=>import(`./powershell-D8WpQPaX.js`),[]),`powershell`),psm1:()=>a(()=>r(()=>import(`./powershell-D8WpQPaX.js`),[]),`powershell`),rb:()=>a(()=>r(()=>import(`./ruby-ChfNwlCY.js`),[]),`ruby`),swift:()=>a(()=>r(()=>import(`./swift-DKB6_1j6.js`),[]),`swift`),toml:()=>a(()=>r(()=>import(`./toml-BPTmHmyx.js`),[]),`toml`),ini:()=>a(()=>r(()=>import(`./properties-C357ku5U.js`),[]),`properties`),cfg:()=>a(()=>r(()=>import(`./properties-C357ku5U.js`),[]),`properties`),conf:()=>a(()=>r(()=>import(`./properties-C357ku5U.js`),[]),`properties`),properties:()=>a(()=>r(()=>import(`./properties-C357ku5U.js`),[]),`properties`),dockerfile:()=>a(()=>r(()=>import(`./dockerfile-BDePf5PZ.js`),[]),`dockerfile`),diff:()=>a(()=>r(()=>import(`./diff-ChtP43wD.js`),[]),`diff`),patch:()=>a(()=>r(()=>import(`./diff-ChtP43wD.js`),[]),`diff`)},s={cpp:`C++`,cc:`C++`,cxx:`C++`,hpp:`C++`,hh:`C++`,hxx:`C++`,cu:`CUDA C++`,cuh:`CUDA C++`,c:`C`,h:`C/C++ Header`,js:`JavaScript`,jsx:`JSX`,mjs:`JavaScript`,cjs:`JavaScript`,ts:`TypeScript`,tsx:`TSX`,mts:`TypeScript`,cts:`TypeScript`,py:`Python`,pyi:`Python Stub`,java:`Java`,php:`PHP`,sql:`SQL`,yaml:`YAML`,yml:`YAML`,xml:`XML`,json:`JSON`,jsonc:`JSON with Comments`,json5:`JSON5`,css:`CSS`,md:`Markdown`,markdown:`Markdown`,mdx:`MDX`,rs:`Rust`,go:`Go`,html:`HTML`,htm:`HTML`,vue:`Vue`,svelte:`Svelte`,sh:`Shell`,bash:`Bash`,zsh:`Zsh`,fish:`Fish`,ps1:`PowerShell`,psm1:`PowerShell`,rb:`Ruby`,swift:`Swift`,toml:`TOML`,ini:`INI`,cfg:`Config`,conf:`Config`,properties:`Properties`,dockerfile:`Dockerfile`,diff:`Diff`,patch:`Patch`};function c(e){return o[e.toLowerCase()]}function l(e){return s[e.toLowerCase()]}var u=t(),d=262144,f=131072;function p(e){return e.split(`.`).pop()?.toLowerCase()??``}function m(e){return getComputedStyle(document.documentElement).getPropertyValue(e).trim()}function h(e,t){return e.HighlightStyle.define([{tag:t.tags.keyword,color:m(`--cm-keyword`)},{tag:t.tags.string,color:m(`--cm-string`)},{tag:t.tags.number,color:m(`--cm-number`)},{tag:t.tags.comment,color:m(`--cm-comment`),fontStyle:`italic`},{tag:t.tags.function(t.tags.variableName),color:m(`--cm-function`)},{tag:t.tags.typeName,color:m(`--cm-type`)},{tag:t.tags.className,color:m(`--cm-type`)},{tag:t.tags.propertyName,color:m(`--cm-property`)},{tag:t.tags.operator,color:m(`--cm-operator`)},{tag:t.tags.variableName,color:m(`--cm-variable`)}])}function g({path:e,value:t,onChange:n,jumpToLine:a,onSave:o}){let s=(0,i.useRef)(null),m=(0,i.useRef)(null),g=(0,i.useRef)(null),_=(0,i.useRef)(null),v=(0,i.useRef)(null),y=(0,i.useRef)(n),b=(0,i.useRef)(o);y.current=n,b.current=o;let[x,S]=(0,i.useState)(!1),[C,w]=(0,i.useState)(!1),[T,E]=(0,i.useState)(`0,0`);(0,i.useEffect)(()=>{let n=!1,i,a,o,l;return(async()=>{try{let u=p(e),f=c(u),[x,C,T,D,O,k,A]=await Promise.all([r(()=>import(`./dist-hfpd-aWa.js`).then(e=>e.x),__vite__mapDeps([1,2,3])),r(()=>import(`./dist-B68Q8uXX.js`).then(e=>e.s),__vite__mapDeps([4,2,3,1])),r(()=>import(`./dist-DP2ygAFF.js`),__vite__mapDeps([22,1,2,3,4,5,0])),r(()=>import(`./dist-C9AkgNbt.js`),__vite__mapDeps([23,1,2,3,4])),r(()=>import(`./dist-DxoWS5MH.js`),__vite__mapDeps([0,1,2,3,4,5])),r(()=>import(`./dist-CpuexUlh.js`).then(e=>e.n),__vite__mapDeps([5,2,3])),f?f().catch(e=>(console.warn(`[CodeMirror] Failed to load language for .${u}:`,e),null)):Promise.resolve(null)]);if(n||!s.current)return;o=O,l=k,_.current=x.Annotation.define(),v.current=x.Transaction.addToHistory;let j=t.length<=d&&A!==null;a=new x.Compartment;let M=[C.lineNumbers(),C.highlightActiveLine(),C.drawSelection(),T.history(),C.keymap.of([T.indentWithTab,...T.defaultKeymap,...T.historyKeymap,...D.searchKeymap,{key:`Mod-s`,run:()=>(b.current(),!0)}]),C.EditorView.updateListener.of(e=>{e.docChanged&&!(_.current&&e.transactions.some(e=>e.annotation(_.current)===!0))&&y.current(e.state.doc.toString()),e.selectionSet&&E(`${e.state.selection.main.from},${e.state.selection.main.to}`)}),C.EditorView.domEventHandlers({mousedown:(e,t)=>(t.focus(),!1)})];if(A)try{M.push(A)}catch(e){console.warn(`[CodeMirror] Failed to apply language extension for .${u}:`,e),j=!1}if(j&&a&&o&&l)try{M.push(a.of(o.syntaxHighlighting(h(o,l))))}catch(e){console.warn(`[CodeMirror] Failed to attach syntaxHighlighting for .${u}:`,e),j=!1}if(i=new C.EditorView({state:x.EditorState.create({doc:t,extensions:M}),parent:s.current}),n){i.destroy();return}m.current=i,S(j),w(!0);let N=new MutationObserver(()=>{let e=m.current;if(e&&j&&a&&o&&l)try{e.dispatch({effects:a.reconfigure(o.syntaxHighlighting(h(o,l)))})}catch{}});g.current=N,N.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-theme`]})}catch(e){console.error(`[CodeMirror] Fatal editor initialization error:`,e),n||w(!0)}})(),()=>{n=!0,g.current?.disconnect(),g.current=null,i?.destroy(),m.current=null,w(!1),s.current&&(s.current.textContent=``)}},[e]),(0,i.useEffect)(()=>{let e=m.current;if(!e)return;let n=e.state.doc.toString();n!==t&&e.dispatch({changes:{from:0,to:n.length,insert:t},annotations:_.current?_.current.of(!0):void 0})},[t]),(0,i.useEffect)(()=>{let e=m.current;if(!e||!a)return;let t=e.state.doc.line(Math.min(Math.max(1,a.line),e.state.doc.lines));e.dispatch({selection:{anchor:t.from,head:t.to},scrollIntoView:!0}),e.focus()},[a,C]);let D=m.current?.state.doc.toString()??t,O=D.length<=f?D:D.slice(0,f),k=p(e),A=l(k);return(0,u.jsxs)(`div`,{className:`file-cm-wrap`,"data-file-text":e,"data-file-content":O,"data-file-content-truncated":D.length>f?`1`:`0`,"data-file-selection":T,"data-file-editor-ready":C?`1`:`0`,onMouseDown:()=>m.current?.focus(),children:[(0,u.jsx)(`div`,{className:`file-cm`,ref:s,"aria-label":`编辑 ${e}`}),!C&&(0,u.jsx)(`span`,{className:`file-cm-status`,"data-file-editor-loading":`1`,children:`⌛ 正在加载编辑器…`}),C&&!x&&(0,u.jsx)(`span`,{className:`file-cm-status`,"data-file-highlight-status":`plain`,children:`纯文本`}),C&&x&&A&&(0,u.jsx)(`span`,{className:`file-cm-status file-cm-status-lang`,"data-file-highlight-status":k,children:A})]})}export{g as CodeMirrorEditor};