@agimon-ai/po-file-edit 0.0.0-alpha.0 → 0.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/apiContracts.cjs +1 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +1 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/extensions/native.mjs +51 -0
- package/dist/extensions/native.mjs.map +1 -0
- package/dist/extensions/server.cjs +319 -95
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +1 -1
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +346 -123
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +79 -397
- package/dist/extensions/web.mjs.map +1 -1
- package/package.json +47 -18
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ tree comparison. See [What this cannot see](#what-this-cannot-see) for gaps in t
|
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
21
|
-
`po init`
|
|
21
|
+
`po init` include File Edit in `default.packages` in `.po/modes.yaml`.
|
|
22
22
|
Remove it or move it to a named layer to change which major modes load it.
|
|
23
23
|
|
|
24
24
|
| Entry | Purpose |
|
|
@@ -62,7 +62,7 @@ and keeps it, content-addressed, beside the session timeline; both go when the s
|
|
|
62
62
|
|
|
63
63
|
The timeline contains file paths, and the snapshot store holds copies of the file content the
|
|
64
64
|
session changed. That content can be sensitive. Both live under the Pi agent directory,
|
|
65
|
-
`$PI_CODING_AGENT_DIR` or `~/.po/agent/
|
|
65
|
+
`$PI_CODING_AGENT_DIR` or `~/.po/agent/po-file-edit`, never inside the working tree, and should be
|
|
66
66
|
protected with the same access controls as Pi session data.
|
|
67
67
|
|
|
68
68
|
A parent session removes its own timeline and snapshots on final disposal. Child sessions share the parent timeline and do not clear it. Activity selection stops retain review data for reactivation. A session that is killed cannot, so
|
package/dist/apiContracts.cjs
CHANGED
|
@@ -141,7 +141,7 @@ const apiContracts = (0, _agimon_ai_po_core_apiContracts.defineApiContract)({
|
|
|
141
141
|
sockets: ["global", "workspace"].map((scope) => ({
|
|
142
142
|
id: "files.channel",
|
|
143
143
|
scope,
|
|
144
|
-
service: "po.hub
|
|
144
|
+
service: "po.hub",
|
|
145
145
|
member: "file_edits",
|
|
146
146
|
kind: "channel",
|
|
147
147
|
direction: "server-to-client",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.cjs","names":["Type","defineApiContract","jsonApiResponses"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Tool = literals(['edit', 'write', 'bash', 'user']);\nconst Hunk = Type.Object({\n start: N,\n rows: Type.Array(Type.Object({ marker: literals(['+', '-', ' ']), line: N, content: S })),\n});\nconst Diff = { additions: N, removals: N, hunks: O(Type.Array(Hunk)), note: O(S) };\nconst Working = Type.Object({ content: S, hash: S, unavailable: B, reason: O(S) });\nexport const FileDetailSchema = Type.Object({\n path: S,\n relPath: S,\n versions: Type.Array(Type.Object({ index: N, tool: Tool, at: N, origin: literals(['tool', 'scan']), ...Diff })),\n cumulative: Type.Object(Diff),\n working: Working,\n});\nexport const FilesPayloadSchema = Type.Object({\n items: Type.Array(Type.Object({ path: S, relPath: S, tool: Tool, at: N, count: N, diffable: B })),\n});\nconst query = [{ name: 'path', in: 'query' as const, required: true, schema: Type.String({ minLength: 1 }) }];\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'files.detail',\n scope: 'session',\n basePath: 'file-edits',\n path: '/detail',\n method: 'GET',\n authentication: 'owner',\n description: 'Read recorded changes and current file content.',\n parameters: query,\n responses: jsonApiResponses(FileDetailSchema),\n },\n {\n id: 'files.preview',\n scope: 'session',\n basePath: 'file-edits',\n path: '/preview',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a file contained in the session directory.',\n parameters: query,\n responses: jsonApiResponses(Type.Object({ path: S, relPath: S, working: Working })),\n },\n {\n id: 'files.save',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'PUT',\n authentication: 'owner',\n description: 'Save a recorded file if its hash still matches.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ path: S, expectedHash: S, content: S }),\n },\n responses: jsonApiResponses(Type.Object({ hash: S })),\n },\n {\n id: 'files.delete',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a recorded file.',\n parameters: query,\n responses: {\n '204': { description: 'File deleted or already absent.' },\n '404': { description: 'Unrecorded file.', schema: Type.Object({ error: S }) },\n '409': { description: 'Deletion failed.', schema: Type.Object({ error: S }) },\n },\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'files.channel',\n scope,\n service: 'po.hub
|
|
1
|
+
{"version":3,"file":"apiContracts.cjs","names":["Type","defineApiContract","jsonApiResponses"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Tool = literals(['edit', 'write', 'bash', 'user']);\nconst Hunk = Type.Object({\n start: N,\n rows: Type.Array(Type.Object({ marker: literals(['+', '-', ' ']), line: N, content: S })),\n});\nconst Diff = { additions: N, removals: N, hunks: O(Type.Array(Hunk)), note: O(S) };\nconst Working = Type.Object({ content: S, hash: S, unavailable: B, reason: O(S) });\nexport const FileDetailSchema = Type.Object({\n path: S,\n relPath: S,\n versions: Type.Array(Type.Object({ index: N, tool: Tool, at: N, origin: literals(['tool', 'scan']), ...Diff })),\n cumulative: Type.Object(Diff),\n working: Working,\n});\nexport const FilesPayloadSchema = Type.Object({\n items: Type.Array(Type.Object({ path: S, relPath: S, tool: Tool, at: N, count: N, diffable: B })),\n});\nconst query = [{ name: 'path', in: 'query' as const, required: true, schema: Type.String({ minLength: 1 }) }];\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'files.detail',\n scope: 'session',\n basePath: 'file-edits',\n path: '/detail',\n method: 'GET',\n authentication: 'owner',\n description: 'Read recorded changes and current file content.',\n parameters: query,\n responses: jsonApiResponses(FileDetailSchema),\n },\n {\n id: 'files.preview',\n scope: 'session',\n basePath: 'file-edits',\n path: '/preview',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a file contained in the session directory.',\n parameters: query,\n responses: jsonApiResponses(Type.Object({ path: S, relPath: S, working: Working })),\n },\n {\n id: 'files.save',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'PUT',\n authentication: 'owner',\n description: 'Save a recorded file if its hash still matches.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ path: S, expectedHash: S, content: S }),\n },\n responses: jsonApiResponses(Type.Object({ hash: S })),\n },\n {\n id: 'files.delete',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a recorded file.',\n parameters: query,\n responses: {\n '204': { description: 'File deleted or already absent.' },\n '404': { description: 'Unrecorded file.', schema: Type.Object({ error: S }) },\n '409': { description: 'Deletion failed.', schema: Type.Object({ error: S }) },\n },\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'files.channel',\n scope,\n service: 'po.hub',\n member: 'file_edits',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'File edits channel payload in the hub frame payload property.',\n input: FilesPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
package/dist/apiContracts.mjs
CHANGED
|
@@ -137,7 +137,7 @@ const apiContracts = defineApiContract({
|
|
|
137
137
|
sockets: ["global", "workspace"].map((scope) => ({
|
|
138
138
|
id: "files.channel",
|
|
139
139
|
scope,
|
|
140
|
-
service: "po.hub
|
|
140
|
+
service: "po.hub",
|
|
141
141
|
member: "file_edits",
|
|
142
142
|
kind: "channel",
|
|
143
143
|
direction: "server-to-client",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Tool = literals(['edit', 'write', 'bash', 'user']);\nconst Hunk = Type.Object({\n start: N,\n rows: Type.Array(Type.Object({ marker: literals(['+', '-', ' ']), line: N, content: S })),\n});\nconst Diff = { additions: N, removals: N, hunks: O(Type.Array(Hunk)), note: O(S) };\nconst Working = Type.Object({ content: S, hash: S, unavailable: B, reason: O(S) });\nexport const FileDetailSchema = Type.Object({\n path: S,\n relPath: S,\n versions: Type.Array(Type.Object({ index: N, tool: Tool, at: N, origin: literals(['tool', 'scan']), ...Diff })),\n cumulative: Type.Object(Diff),\n working: Working,\n});\nexport const FilesPayloadSchema = Type.Object({\n items: Type.Array(Type.Object({ path: S, relPath: S, tool: Tool, at: N, count: N, diffable: B })),\n});\nconst query = [{ name: 'path', in: 'query' as const, required: true, schema: Type.String({ minLength: 1 }) }];\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'files.detail',\n scope: 'session',\n basePath: 'file-edits',\n path: '/detail',\n method: 'GET',\n authentication: 'owner',\n description: 'Read recorded changes and current file content.',\n parameters: query,\n responses: jsonApiResponses(FileDetailSchema),\n },\n {\n id: 'files.preview',\n scope: 'session',\n basePath: 'file-edits',\n path: '/preview',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a file contained in the session directory.',\n parameters: query,\n responses: jsonApiResponses(Type.Object({ path: S, relPath: S, working: Working })),\n },\n {\n id: 'files.save',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'PUT',\n authentication: 'owner',\n description: 'Save a recorded file if its hash still matches.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ path: S, expectedHash: S, content: S }),\n },\n responses: jsonApiResponses(Type.Object({ hash: S })),\n },\n {\n id: 'files.delete',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a recorded file.',\n parameters: query,\n responses: {\n '204': { description: 'File deleted or already absent.' },\n '404': { description: 'Unrecorded file.', schema: Type.Object({ error: S }) },\n '409': { description: 'Deletion failed.', schema: Type.Object({ error: S }) },\n },\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'files.channel',\n scope,\n service: 'po.hub
|
|
1
|
+
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract, jsonApiResponses } from '@agimon-ai/po-core/apiContracts';\nimport { Type } from 'typebox';\n\nconst S = Type.String();\nconst N = Type.Number();\nconst B = Type.Boolean();\nconst O = Type.Optional;\nconst literals = <T extends string>(values: T[]) => Type.Union(values.map((value) => Type.Literal(value)));\nconst Tool = literals(['edit', 'write', 'bash', 'user']);\nconst Hunk = Type.Object({\n start: N,\n rows: Type.Array(Type.Object({ marker: literals(['+', '-', ' ']), line: N, content: S })),\n});\nconst Diff = { additions: N, removals: N, hunks: O(Type.Array(Hunk)), note: O(S) };\nconst Working = Type.Object({ content: S, hash: S, unavailable: B, reason: O(S) });\nexport const FileDetailSchema = Type.Object({\n path: S,\n relPath: S,\n versions: Type.Array(Type.Object({ index: N, tool: Tool, at: N, origin: literals(['tool', 'scan']), ...Diff })),\n cumulative: Type.Object(Diff),\n working: Working,\n});\nexport const FilesPayloadSchema = Type.Object({\n items: Type.Array(Type.Object({ path: S, relPath: S, tool: Tool, at: N, count: N, diffable: B })),\n});\nconst query = [{ name: 'path', in: 'query' as const, required: true, schema: Type.String({ minLength: 1 }) }];\nexport const apiContracts = defineApiContract({\n version: 1,\n dynamic: [],\n http: [\n {\n id: 'files.detail',\n scope: 'session',\n basePath: 'file-edits',\n path: '/detail',\n method: 'GET',\n authentication: 'owner',\n description: 'Read recorded changes and current file content.',\n parameters: query,\n responses: jsonApiResponses(FileDetailSchema),\n },\n {\n id: 'files.preview',\n scope: 'session',\n basePath: 'file-edits',\n path: '/preview',\n method: 'GET',\n authentication: 'owner',\n description: 'Read a file contained in the session directory.',\n parameters: query,\n responses: jsonApiResponses(Type.Object({ path: S, relPath: S, working: Working })),\n },\n {\n id: 'files.save',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'PUT',\n authentication: 'owner',\n description: 'Save a recorded file if its hash still matches.',\n body: {\n required: true,\n contentType: 'application/json',\n schema: Type.Object({ path: S, expectedHash: S, content: S }),\n },\n responses: jsonApiResponses(Type.Object({ hash: S })),\n },\n {\n id: 'files.delete',\n scope: 'session',\n basePath: 'file-edits',\n path: '/content',\n method: 'DELETE',\n authentication: 'owner',\n description: 'Delete a recorded file.',\n parameters: query,\n responses: {\n '204': { description: 'File deleted or already absent.' },\n '404': { description: 'Unrecorded file.', schema: Type.Object({ error: S }) },\n '409': { description: 'Deletion failed.', schema: Type.Object({ error: S }) },\n },\n },\n ],\n sockets: (['global', 'workspace'] as const).map((scope) => ({\n id: 'files.channel',\n scope,\n service: 'po.hub',\n member: 'file_edits',\n kind: 'channel',\n direction: 'server-to-client',\n description: 'File edits channel payload in the hub frame payload property.',\n input: FilesPayloadSchema,\n })),\n});\nexport default apiContracts;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { defineChannelFile, defineNativePlugin, defineSessionStore } from "@agimon-ai/po-core/native";
|
|
2
|
+
//#region src/types/webFiles.ts
|
|
3
|
+
/** The frame type the hub publishes and the web plugin claims; declared in the poWeb block too. */
|
|
4
|
+
const filesChannelType = "file_edits";
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_lib/filesModel.ts
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function isItem(value) {
|
|
11
|
+
return isRecord(value) && typeof value.path === "string" && typeof value.relPath === "string" && typeof value.tool === "string" && typeof value.at === "number" && typeof value.count === "number";
|
|
12
|
+
}
|
|
13
|
+
/** The shared wire gate for browser and native stores. */
|
|
14
|
+
function parseFilesPayload(input) {
|
|
15
|
+
if (!isRecord(input) || !Array.isArray(input.items)) return null;
|
|
16
|
+
return { items: input.items.filter(isItem) };
|
|
17
|
+
}
|
|
18
|
+
/** The plugin's session data channel: 'file_edits' payloads into the store. */
|
|
19
|
+
const filesChannel = defineSessionStore({
|
|
20
|
+
items: [],
|
|
21
|
+
detail: {},
|
|
22
|
+
loading: [],
|
|
23
|
+
errors: {},
|
|
24
|
+
comments: []
|
|
25
|
+
}).channel({
|
|
26
|
+
channel: filesChannelType,
|
|
27
|
+
parse: parseFilesPayload,
|
|
28
|
+
reduce: (current, { items }) => ({
|
|
29
|
+
...current,
|
|
30
|
+
items
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/extensions/workspaces/sessions/(frontend)/channel/file-edits.native.ts
|
|
35
|
+
var file_edits_native_default = defineChannelFile(filesChannel);
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region generated/native.ts
|
|
38
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
39
|
+
...identity,
|
|
40
|
+
...value
|
|
41
|
+
};
|
|
42
|
+
const nativePlugin = defineNativePlugin({
|
|
43
|
+
id: "files",
|
|
44
|
+
session: { get channels() {
|
|
45
|
+
return [via({ channel: "file_edits" }, file_edits_native_default)];
|
|
46
|
+
} }
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
export { nativePlugin };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=native.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.mjs","names":["channelFileEdits"],"sources":["../../src/types/webFiles.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/filesModel.ts","../../src/extensions/workspaces/sessions/(frontend)/_lib/filesStore.native.ts","../../src/extensions/workspaces/sessions/(frontend)/channel/file-edits.native.ts","../../generated/native.ts"],"sourcesContent":["import type { FileEditTool } from './domain';\n\n/**\n * View types shared by this package's hub channel and its web plugin. Wire\n * JSON only, no node types, so the cockpit bundle can carry them; the\n * cockpit receives them as 'file_edits' channel payloads.\n *\n * The channel carries the list alone. One file's diffs and content are large\n * and only wanted once a reader opens the file, so those come from the session\n * API instead (see fileEditsApi.ts).\n */\n\n/** One file this session changed, as the activity dock lists it. */\nexport interface FilesItemView {\n /** Absolute path, and the identity every API route takes back. */\n path: string;\n /** Path relative to the session's working directory, which is what a row shows. */\n relPath: string;\n /** The tool behind the most recent change. */\n tool: FileEditTool;\n /** When that change happened, epoch milliseconds. */\n at: number;\n /** How many changes this session recorded for the file. */\n count: number;\n /**\n * Whether any change captured a baseline, so a diff can be shown at all. A\n * file found by comparing the tree around a bash call has no \"before\", so it\n * is listed and readable but never diffed.\n */\n diffable: boolean;\n}\n\n/** The frame type the hub publishes and the web plugin claims; declared in the poWeb block too. */\nexport const filesChannelType = 'file_edits';\n\n/** Footer status key whose presence shows the activity group; the extension publishes it. */\nexport const filesStatusKey = 'po-file-edit-files';\n","import type { FilesItemView } from '../../../../../types/webFiles';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isItem(value: unknown): value is FilesItemView {\n return (\n isRecord(value) &&\n typeof value.path === 'string' &&\n typeof value.relPath === 'string' &&\n typeof value.tool === 'string' &&\n typeof value.at === 'number' &&\n typeof value.count === 'number'\n );\n}\n\nexport interface FilesPayload {\n items: FilesItemView[];\n}\n\n/** The shared wire gate for browser and native stores. */\nexport function parseFilesPayload(input: unknown): FilesPayload | null {\n if (!isRecord(input) || !Array.isArray(input.items)) return null;\n return { items: input.items.filter(isItem) };\n}\n","import { defineSessionStore } from '@agimon-ai/po-core/native';\nimport type { ReviewComment } from '@agimon-ai/po-web-components';\n\nimport type { FileEditsDetailView } from '../../../../../types/fileEditsApi';\nimport { filesChannelType, type FilesItemView } from '../../../../../types/webFiles';\nimport { parseFilesPayload, type FilesPayload } from './filesModel';\nexport type { FilesPayload } from './filesModel';\n\n/**\n * One session's record: the hub's last report plus whatever this page does with\n * it. Records are immutable values; the reducer and every action return a new one.\n *\n * Detail is cached per file rather than per open tab, because a reader closes a\n * file and comes back to it, and refetching the same history for the same click\n * is work the session already did.\n */\nexport interface FilesSession {\n items: FilesItemView[];\n /** One file's fetched history, keyed by absolute path. */\n detail: Readonly<Record<string, FileEditsDetailView>>;\n /** Paths with a detail request in flight, so a panel can say it is loading. */\n loading: readonly string[];\n /** What a failed fetch or a refused save reported, keyed by absolute path. */\n errors: Readonly<Record<string, string>>;\n /** Review notes waiting to be sent, in the order they were written. */\n comments: readonly ReviewComment[];\n}\n\nconst empty: FilesSession = { items: [], detail: {}, loading: [], errors: {}, comments: [] };\n\nexport const files = defineSessionStore<FilesSession>(empty);\n\n/** The plugin's session data channel: 'file_edits' payloads into the store. */\nexport const filesChannel = files.channel<FilesPayload>({\n channel: filesChannelType,\n parse: parseFilesPayload,\n // Only the list is replaced. The cached detail and the pending notes belong\n // to files a reader opened, not to the list: a file the session deletes stops\n // being listed, and pruning here would blank the tab still open on it and\n // then refetch it on the next frame.\n reduce: (current, { items }) => ({ ...current, items }),\n});\n\nexport function markLoading(sessionId: string, filePath: string): void {\n files.update(sessionId, (current) =>\n current.loading.includes(filePath)\n ? current\n : { ...current, loading: [...current.loading, filePath], errors: withoutKey(current.errors, filePath) },\n );\n}\n\nexport function storeDetail(sessionId: string, detail: FileEditsDetailView): void {\n files.update(sessionId, (current) => ({\n ...current,\n detail: { ...current.detail, [detail.path]: detail },\n loading: current.loading.filter((path) => path !== detail.path),\n errors: withoutKey(current.errors, detail.path),\n }));\n}\n\nexport function storeError(sessionId: string, filePath: string, message: string): void {\n files.update(sessionId, (current) => ({\n ...current,\n loading: current.loading.filter((path) => path !== filePath),\n errors: { ...current.errors, [filePath]: message },\n }));\n}\n\nexport function addComment(sessionId: string, comment: ReviewComment): void {\n files.update(sessionId, (current) => ({ ...current, comments: [...current.comments, comment] }));\n}\n\nexport function removeComment(sessionId: string, id: string): void {\n files.update(sessionId, (current) => ({\n ...current,\n comments: current.comments.filter((comment) => comment.id !== id),\n }));\n}\n\nexport function clearComments(sessionId: string, filePath: string): void {\n files.update(sessionId, (current) => ({\n ...current,\n comments: current.comments.filter((comment) => comment.path !== filePath),\n }));\n}\n\nfunction withoutKey(source: Readonly<Record<string, string>>, key: string): Record<string, string> {\n return Object.fromEntries(Object.entries(source).filter(([entry]) => entry !== key));\n}\n","import { defineChannelFile } from '@agimon-ai/po-core/native';\n\nimport { filesChannel } from '../_lib/filesStore.native';\nexport default defineChannelFile(filesChannel);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineNativePlugin, type WebPluginContributions } from '@agimon-ai/po-core/native';\n\nimport channelFileEdits from '../src/extensions/workspaces/sessions/(frontend)/channel/file-edits.native';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const nativePlugin = defineNativePlugin({\n id: 'files',\n session: {\n get channels(): WebPluginContributions['channels'] { return [via({ channel: 'file_edits' }, channelFileEdits)]; },\n },\n});\n"],"mappings":";;;AAiCA,MAAa,mBAAmB;;;AC/BhC,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,OAAO,OAAwC;CACtD,OACE,SAAS,KAAK,KACd,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,UAAU;AAE3B;;AAOA,SAAgB,kBAAkB,OAAqC;CACrE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG,OAAO;CAC5D,OAAO,EAAE,OAAO,MAAM,MAAM,OAAO,MAAM,EAAE;AAC7C;;ACQA,MAAa,eAHQ,mBAAiC;CAFxB,OAAO,CAAC;CAAG,QAAQ,CAAC;CAAG,SAAS,CAAC;CAAG,QAAQ,CAAC;CAAG,UAAU,CAAC;AAEnC,CAG1B,CAAA,CAAM,QAAsB;CACtD,SAAS;CACT,OAAO;CAKP,SAAS,SAAS,EAAE,aAAa;EAAE,GAAG;EAAS;CAAM;AACvD,CAAC;;;ACtCD,IAAA,4BAAe,kBAAkB,YAAY;;;ACE7C,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,eAAe,mBAAmB;CAC7C,IAAI;CACJ,SAAS,EACP,IAAI,WAA+C;EAAE,OAAO,CAAC,IAAI,EAAE,SAAS,aAAa,GAAGA,yBAAgB,CAAC;CAAG,EAClH;AACF,CAAC"}
|