copilot-operator 0.1.13 → 0.1.15

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 (77) hide show
  1. package/README.md +9 -14
  2. package/dist/src/api/operator.controller.js +31 -42
  3. package/dist/src/api/operator.service.js +663 -367
  4. package/dist/src/api/saveToDesktop.js +1 -1
  5. package/dist/src/api/security.js +40 -10
  6. package/dist/src/api/server.js +75 -12
  7. package/dist/src/api/settings.js +156 -15
  8. package/dist/src/cli.js +108 -97
  9. package/dist/src/config/handWritten.js +36 -0
  10. package/dist/src/config/layout.js +13 -4
  11. package/dist/src/config/lockedPolicy.js +56 -22
  12. package/dist/src/config/schema.js +13 -84
  13. package/dist/src/context/desktopDir.js +44 -0
  14. package/dist/src/exec/authorizer.js +9 -1
  15. package/dist/src/exec/checks.js +240 -23
  16. package/dist/src/exec/dangerous.js +29 -5
  17. package/dist/src/exec/network.js +105 -28
  18. package/dist/src/exec/redaction.js +52 -9
  19. package/dist/src/exec/reportFile.js +4 -5
  20. package/dist/src/exec/runner.js +146 -13
  21. package/dist/src/exec/shells.js +41 -6
  22. package/dist/src/exec/workDir.js +4 -4
  23. package/dist/src/orchestrator/derivedChecks.js +1 -0
  24. package/dist/src/orchestrator/review.js +25 -3
  25. package/dist/src/orchestrator/taskRunner.js +476 -163
  26. package/dist/src/plan/brief.js +37 -44
  27. package/dist/src/plan/importPlan.js +15 -7
  28. package/dist/src/plan/schema.js +64 -29
  29. package/dist/src/plan/script.js +0 -15
  30. package/dist/src/plan/systemGuide.js +2 -177
  31. package/dist/src/protocol/parser.js +5 -1
  32. package/dist/src/session/debugExport.js +1 -1
  33. package/dist/src/session/exportRecord.js +1 -1
  34. package/dist/src/session/exports.js +49 -9
  35. package/dist/src/session/store.js +215 -80
  36. package/dist/src/vcs/scope.js +29 -6
  37. package/dist/src/vcs/taskVcs.js +78 -10
  38. package/dist/web/404.html +2 -2
  39. package/dist/web/_next/static/chunks/36-94660f1cde2da529.js +1 -0
  40. package/dist/web/_next/static/chunks/549-9548d9a132c8c7d4.js +1 -0
  41. package/dist/web/_next/static/chunks/867-8b95ae984befc0e2.js +1 -0
  42. package/dist/web/_next/static/chunks/app/defaults/page-71a43ed69134b48d.js +1 -0
  43. package/dist/web/_next/static/chunks/app/sessions/view/page-886f5fc99e146408.js +1 -0
  44. package/dist/web/_next/static/css/{a0642fdb8adf9edb.css → e2a2c9eb3c226313.css} +1 -1
  45. package/dist/web/appearance.html +2 -2
  46. package/dist/web/appearance.txt +12 -12
  47. package/dist/web/defaults.html +2 -2
  48. package/dist/web/defaults.txt +12 -12
  49. package/dist/web/history.html +2 -2
  50. package/dist/web/history.txt +12 -12
  51. package/dist/web/import.html +2 -2
  52. package/dist/web/import.txt +12 -12
  53. package/dist/web/index.html +2 -2
  54. package/dist/web/index.txt +12 -12
  55. package/dist/web/level1.html +2 -2
  56. package/dist/web/level1.txt +12 -12
  57. package/dist/web/presets.html +2 -2
  58. package/dist/web/presets.txt +12 -12
  59. package/dist/web/sessions/view.html +2 -2
  60. package/dist/web/sessions/view.txt +12 -12
  61. package/dist/web/system.html +2 -2
  62. package/dist/web/system.txt +12 -12
  63. package/package.json +18 -10
  64. package/prompts/level1.md +2 -1
  65. package/prompts/organisation.bg.md +0 -1
  66. package/prompts/organisation.en.md +0 -1
  67. package/dist/src/context/contextFiles.js +0 -295
  68. package/dist/src/context/desktopMirror.js +0 -173
  69. package/dist/src/context/projectMirror.js +0 -432
  70. package/dist/web/_next/static/chunks/295-04151ded0741d18d.js +0 -1
  71. package/dist/web/_next/static/chunks/36-c3dd06c6a459c114.js +0 -1
  72. package/dist/web/_next/static/chunks/549-a5a59a94f92bb3eb.js +0 -1
  73. package/dist/web/_next/static/chunks/867-84c19f047972ffc3.js +0 -1
  74. package/dist/web/_next/static/chunks/app/defaults/page-5f180a88398be4c9.js +0 -1
  75. package/dist/web/_next/static/chunks/app/sessions/view/page-34d7988849f9aa76.js +0 -1
  76. /package/dist/web/_next/static/{NosxirjkOgV-Boyq8dGPl → IwNZiBrEMAcXEUFj2miiU}/_buildManifest.js +0 -0
  77. /package/dist/web/_next/static/{NosxirjkOgV-Boyq8dGPl → IwNZiBrEMAcXEUFj2miiU}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -8,9 +8,8 @@ browser, so it needs no API licence and no admin consent.
8
8
 
9
9
  > **Status: working, every part verified against a live Microsoft 365 Copilot tenant.**
10
10
  > The loop, chat naming, reply parsing, command execution and the results file are confirmed.
11
- > A step printing for 92 s survived while a silent one was stopped at its idle limit. Project files
12
- > mirrored to the Desktop were attached and answered questions from. Unattended mode is the
13
- > one thing still untried, deliberately.
11
+ > A step printing for 92 s survived while a silent one was stopped at its idle limit. Unattended
12
+ > mode is the one thing still untried, deliberately.
14
13
 
15
14
  ## What it does
16
15
 
@@ -26,8 +25,8 @@ browser, so it needs no API licence and no admin consent.
26
25
  6. Repeats until Copilot finishes with a **summary** of what it did and what the result is.
27
26
  7. Runs the next queued task in the same conversation.
28
27
 
29
- A **session** is one conversation with a queue of tasks. Selected parts of a project can be
30
- mirrored to a Desktop folder and attached, so the chat can see the code it is asked about.
28
+ A **session** is one conversation with a queue of tasks. No file of the project is attached to
29
+ the chat: it reads the project by running commands in the project folder, like any other step.
31
30
 
32
31
  There is a web UI for all of this, and a terminal command for a single task.
33
32
 
@@ -42,7 +41,7 @@ are redacted from every report before it is uploaded, always, with `report.redac
42
41
  applied on top. Running it in a dedicated Windows account or Windows Sandbox is recommended.
43
42
 
44
43
  **Where the reports go.** Every report — the terminal output of every step, the check results,
45
- the reviewer's transcripts — and any files the project mirror attaches are uploaded into the
44
+ the reviewer's transcripts — are uploaded into the
46
45
  Copilot chat, which stores them in the signed-in account's OneDrive for Business and in the
47
46
  Copilot conversation history. They stay inside the company's own Microsoft 365 tenant, under its
48
47
  Purview DLP policies, sensitivity labels, retention and eDiscovery; nothing goes to any other
@@ -257,7 +256,7 @@ runs this on managed laptops, because no code change can decide it for them.
257
256
  - *Data protection.* See "Where the reports go" above. Where the law requires it, record the
258
257
  processing (a DPIA where automated tooling that stores every command's output and sends it to
259
258
  an AI service triggers one; works-council consultation where tooling that records employees'
260
- actions requires it). Mirrored source may carry personal data in fixtures and exports.
259
+ actions requires it). Command output can carry personal data from fixtures and exports.
261
260
  - *AI-use policy.* Commands written by a language model execute on the endpoint. Confirm mode
262
261
  keeps a person on every line; unattended mode removes that person and is the decision most
263
262
  AI-governance policies want recorded. `policy.lock.json` can forbid it machine-wide.
@@ -273,8 +272,6 @@ which the operator cannot edit (the same file beside the install is honoured too
273
272
  {
274
273
  "maxMode": "confirm",
275
274
  "requireIsolation": true,
276
- "allowDesktopMirror": false,
277
- "allowEnvFiles": false,
278
275
  "passEnv": [],
279
276
  "allowedPrograms": ["pwsh", "powershell", "cmd", "node", "npm", "npx", "git", "dotnet"],
280
277
  "update": { "requireSigned": true, "remote": "https://git.example.com/tools/copilot-operator.git" }
@@ -426,7 +423,8 @@ exist. What the software leaves, and where:
426
423
  | backups taken by `npm run update` | `data-backups/` in the checkout | delete the folder |
427
424
  | the bot's own Edge profile, with its Microsoft 365 session | `%LOCALAPPDATA%\copilot-operator\edge-profile` | sign out in that window (`cop login --fresh` deletes it) and delete the folder |
428
425
  | the API key the browser was handed | the browser's storage for `localhost:3210` | clear site data for that origin |
429
- | logs saved on request, and the optional project mirror | `Desktop\copilot-operator-logs`, the mirror folder in Settings | delete them; they may already be in OneDrive |
426
+ | logs saved on request | `Desktop\copilot-operator-logs` | delete them; they may already be in OneDrive |
427
+ | the project copies of versions before 0.1.14 | `Desktop\copilot-operator-context` | delete the folder; nothing writes to it any more |
430
428
  | the reports uploaded to Copilot | the account's OneDrive and Copilot chat history | delete the conversations, or let the tenant's retention run |
431
429
  | a machine-wide policy lock, if an administrator placed one | `%ProgramData%\copilot-operator\policy.lock.json` | the administrator removes it |
432
430
 
@@ -480,8 +478,6 @@ npx tsx src/cli.ts run run.yaml
480
478
  |---|---|
481
479
  | `cop login` | opens Edge with the bot profile and waits for you to sign in |
482
480
  | `cop doctor [run.yaml]` | checks Node, Edge, PowerShell, the Desktop, the config |
483
- | `cop dirs <projectRoot>` | lists the directories you can select for the mirror |
484
- | `cop mirror <run.yaml>` | refreshes the Desktop folder without touching the chat |
485
481
  | `cop run <run.yaml>` | one task as a new session; `--unattended` skips the per-step prompt |
486
482
  | `cop chat [run.yaml]` | prints the last run's conversation name and link |
487
483
 
@@ -499,8 +495,7 @@ ordered so the risk climbs slowly, starting with checks that need no account at
499
495
  | Command runner, dual timeouts, process-tree kill | `src/exec/runner.ts` | implemented, checked |
500
496
  | Chat naming and reattach after re-login | `src/transport/chatSession.ts` | implemented, checked |
501
497
  | Covering message for the results file | `src/protocol/reporter.ts` | implemented, checked |
502
- | Desktop folder resolution and guard rails | `src/context/contextFiles.ts` | implemented, checked |
503
- | Project mirror, flattened names, incremental | `src/context/projectMirror.ts` | implemented, checked |
498
+ | Desktop folder resolution (for saved logs) | `src/context/desktopDir.ts` | implemented |
504
499
  | Pacing, send cap, backoff | `src/util/pacing.ts` | implemented, checked |
505
500
  | Playwright transport | `src/transport/copilotTransport.ts` | implemented; selectors verified live |
506
501
  | Reply parser and the JSON contract | `src/protocol/parser.ts`, `replySchema.ts` | implemented, checked |
@@ -10,12 +10,36 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
+ /**
14
+ * The HTTP surface. Thin on purpose: every route hands straight to the service.
15
+ *
16
+ * Bound to localhost only (see main.ts). There is no authentication because there is no
17
+ * network: this API runs on the operator's own machine and drives that machine's browser.
18
+ */
13
19
  import { Body, Controller, Delete, Get, Inject, Param, Post, Put, Query, Res, Sse, NotFoundException, BadRequestException } from '@nestjs/common';
14
20
  import { Observable } from 'rxjs';
15
21
  import { OperatorService } from './operator.service.js';
22
+ import { SettingsUnusableError } from './settings.js';
23
+ import { PolicyLockUnusableError } from '../config/lockedPolicy.js';
16
24
  function fail(e) {
25
+ // A settings file or a policy lock that cannot be used is not the request's fault, and every route
26
+ // that reads the settings meets it, wrapped here or not: it is passed on as it is, for the server
27
+ // to answer the same way on all of them (see `server.ts`).
28
+ if (e instanceof SettingsUnusableError || e instanceof PolicyLockUnusableError)
29
+ throw e;
17
30
  throw new BadRequestException(e.message);
18
31
  }
32
+ /**
33
+ * `fail` for a route that writes its own response, as the downloads do: the request's own problem
34
+ * as plain text with a 400, and a settings file or a policy lock that cannot be used passed on to
35
+ * the server's answer for it. Each download answered both itself, so a broken file came back as a
36
+ * bad request there and as a 409 everywhere else.
37
+ */
38
+ function failText(res, e) {
39
+ if (e instanceof SettingsUnusableError || e instanceof PolicyLockUnusableError)
40
+ throw e;
41
+ res.status(400).send(e.message);
42
+ }
19
43
  /**
20
44
  * A `content-disposition` header a session name cannot break.
21
45
  *
@@ -84,7 +108,7 @@ let OperatorController = class OperatorController {
84
108
  return this.ops.savePreset(name, body?.content ?? '').catch(fail);
85
109
  }
86
110
  async deletePreset(name) {
87
- await this.ops.deletePreset(name);
111
+ await this.ops.deletePreset(name).catch(fail);
88
112
  return { ok: true };
89
113
  }
90
114
  // --- named personas (the import page's second box, saved under a name) -----------------
@@ -103,7 +127,7 @@ let OperatorController = class OperatorController {
103
127
  return this.ops.listSessions();
104
128
  }
105
129
  createSession(body) {
106
- return this.ops.createSession(body?.name ?? '', body?.mirror).catch(fail);
130
+ return this.ops.createSession(body?.name ?? '', body?.projectDir).catch(fail);
107
131
  }
108
132
  async session(id) {
109
133
  const s = await this.ops.getSession(id);
@@ -239,7 +263,7 @@ let OperatorController = class OperatorController {
239
263
  res.send(content);
240
264
  }
241
265
  catch (e) {
242
- res.status(400).send(e.message);
266
+ failText(res, e);
243
267
  }
244
268
  }
245
269
  /**
@@ -258,7 +282,7 @@ let OperatorController = class OperatorController {
258
282
  res.send(content);
259
283
  }
260
284
  catch (e) {
261
- res.status(400).send(e.message);
285
+ failText(res, e);
262
286
  }
263
287
  }
264
288
  /**
@@ -286,7 +310,7 @@ let OperatorController = class OperatorController {
286
310
  res.send(content);
287
311
  }
288
312
  catch (e) {
289
- res.status(400).send(e.message);
313
+ failText(res, e);
290
314
  }
291
315
  }
292
316
  /**
@@ -306,7 +330,7 @@ let OperatorController = class OperatorController {
306
330
  res.send(content);
307
331
  }
308
332
  catch (e) {
309
- res.status(400).send(e.message);
333
+ failText(res, e);
310
334
  }
311
335
  }
312
336
  // --- run control ------------------------------------------------------------------------
@@ -456,9 +480,7 @@ let OperatorController = class OperatorController {
456
480
  }
457
481
  /** Stores it. A field left out is kept; an empty `rootDir` clears the default. */
458
482
  setProject(body) {
459
- return this.ops
460
- .setProject({ rootDir: body?.rootDir, name: body?.name, others: body?.others, mirrorToDesktop: body?.mirrorToDesktop, mirror: body?.mirror, desktop: body?.desktop })
461
- .catch(fail);
483
+ return this.ops.setProject({ rootDir: body?.rootDir, name: body?.name, others: body?.others }).catch(fail);
462
484
  }
463
485
  /**
464
486
  * Whether a folder can be used for version control at all, asked before the choice is made.
@@ -506,11 +528,6 @@ let OperatorController = class OperatorController {
506
528
  return this.ops.metrics();
507
529
  }
508
530
  // --- helpers for the UI -----------------------------------------------------------------
509
- dirs(root, gitignore) {
510
- if (!root)
511
- throw new BadRequestException('root is required');
512
- return this.ops.dirs(root, gitignore !== '0' && gitignore !== 'false');
513
- }
514
531
  /**
515
532
  * Opens the machine's own folder dialog. A POST because it has a visible effect: a window
516
533
  * appears on the operator's desktop and waits for them.
@@ -518,19 +535,6 @@ let OperatorController = class OperatorController {
518
535
  browseFolder(body) {
519
536
  return this.ops.browseFolder(body?.start);
520
537
  }
521
- preview(body) {
522
- if (!body?.rootDir?.trim())
523
- throw new BadRequestException('rootDir is required');
524
- return this.ops
525
- .previewMirror({
526
- rootDir: body.rootDir.trim(),
527
- includeDirs: body.includeDirs ?? [],
528
- excludeDirs: body.excludeDirs ?? [],
529
- respectGitignore: body.respectGitignore ?? true,
530
- includeEnvFiles: body.includeEnvFiles ?? false,
531
- })
532
- .catch(fail);
533
- }
534
538
  };
535
539
  __decorate([
536
540
  Get('health'),
@@ -1057,14 +1061,6 @@ __decorate([
1057
1061
  __metadata("design:paramtypes", []),
1058
1062
  __metadata("design:returntype", Promise)
1059
1063
  ], OperatorController.prototype, "metrics", null);
1060
- __decorate([
1061
- Get('dirs'),
1062
- __param(0, Query('root')),
1063
- __param(1, Query('gitignore')),
1064
- __metadata("design:type", Function),
1065
- __metadata("design:paramtypes", [String, String]),
1066
- __metadata("design:returntype", Promise)
1067
- ], OperatorController.prototype, "dirs", null);
1068
1064
  __decorate([
1069
1065
  Post('browse-folder'),
1070
1066
  __param(0, Body()),
@@ -1072,13 +1068,6 @@ __decorate([
1072
1068
  __metadata("design:paramtypes", [Object]),
1073
1069
  __metadata("design:returntype", Promise)
1074
1070
  ], OperatorController.prototype, "browseFolder", null);
1075
- __decorate([
1076
- Post('mirror/preview'),
1077
- __param(0, Body()),
1078
- __metadata("design:type", Function),
1079
- __metadata("design:paramtypes", [Object]),
1080
- __metadata("design:returntype", Promise)
1081
- ], OperatorController.prototype, "preview", null);
1082
1071
  OperatorController = __decorate([
1083
1072
  Controller(),
1084
1073
  __param(0, Inject(OperatorService)),