crewly 1.20.204 → 1.20.206
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/config/skills/_common/lib.sh +4 -3
- package/config/skills/agent/core/docs-comment/SKILL.md +121 -0
- package/config/skills/agent/core/docs-comment/execute.sh +131 -0
- package/config/skills/agent/core/docs-comment/execute.test.sh +105 -0
- package/config/skills/agent/core/project-tickets/SKILL.md +13 -1
- package/config/skills/agent/core/project-tickets/execute.sh +20 -6
- package/config/skills/agent/core/project-tickets/execute.test.sh +8 -2
- package/dist/backend/backend/src/constants.d.ts +76 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +82 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts +30 -0
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.controller.js +75 -0
- package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts +4 -0
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.js +9 -1
- package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +9 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.d.ts +1 -1
- package/dist/backend/backend/src/controllers/project-tickets/project-tickets.routes.js +1 -1
- package/dist/backend/backend/src/controllers/settings/role.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/settings/role.controller.js +21 -7
- package/dist/backend/backend/src/controllers/settings/role.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/settings/settings.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/settings/settings.controller.js +26 -20
- package/dist/backend/backend/src/controllers/settings/settings.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/skill/skill.controller.d.ts +13 -0
- package/dist/backend/backend/src/controllers/skill/skill.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/skill/skill.controller.js +21 -0
- package/dist/backend/backend/src/controllers/skill/skill.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts +11 -1
- package/dist/backend/backend/src/controllers/system/system-control.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/system/system-control.controller.js +36 -2
- package/dist/backend/backend/src/controllers/system/system-control.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +9 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +28 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/middleware/agent-origin.middleware.d.ts +3 -2
- package/dist/backend/backend/src/middleware/agent-origin.middleware.d.ts.map +1 -1
- package/dist/backend/backend/src/middleware/agent-origin.middleware.js +5 -3
- package/dist/backend/backend/src/middleware/agent-origin.middleware.js.map +1 -1
- package/dist/backend/backend/src/middleware/caller-identity.middleware.d.ts +36 -0
- package/dist/backend/backend/src/middleware/caller-identity.middleware.d.ts.map +1 -1
- package/dist/backend/backend/src/middleware/caller-identity.middleware.js +97 -1
- package/dist/backend/backend/src/middleware/caller-identity.middleware.js.map +1 -1
- package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts +4 -0
- package/dist/backend/backend/src/routes/modules/terminal.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/modules/terminal.routes.js +19 -5
- package/dist/backend/backend/src/routes/modules/terminal.routes.js.map +1 -1
- package/dist/backend/backend/src/scripts/input-guard-classify.d.ts +44 -0
- package/dist/backend/backend/src/scripts/input-guard-classify.d.ts.map +1 -0
- package/dist/backend/backend/src/scripts/input-guard-classify.js +116 -0
- package/dist/backend/backend/src/scripts/input-guard-classify.js.map +1 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +8 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +10 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts +8 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js +21 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +13 -27
- package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/google/docs-comments.service.d.ts +171 -0
- package/dist/backend/backend/src/services/google/docs-comments.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/google/docs-comments.service.js +218 -0
- package/dist/backend/backend/src/services/google/docs-comments.service.js.map +1 -0
- package/dist/backend/backend/src/services/google/google-api.client.d.ts +8 -0
- package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
- package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts +10 -0
- package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-workspace-token.service.js +10 -0
- package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/forced-delivery.d.ts +75 -0
- package/dist/backend/backend/src/services/messaging/forced-delivery.d.ts.map +1 -0
- package/dist/backend/backend/src/services/messaging/forced-delivery.js +87 -0
- package/dist/backend/backend/src/services/messaging/forced-delivery.js.map +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts +130 -4
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js +115 -3
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-decision.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.d.ts +122 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.d.ts.map +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.js +274 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-goal.js.map +1 -0
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts +34 -2
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js +59 -2
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-messages.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-retro.js +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-retro.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.d.ts +6 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.js +9 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-stats.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts +13 -3
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js +28 -2
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot-trace.js.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts +92 -7
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js +316 -41
- package/dist/backend/backend/src/services/project-tickets/ticket-autopilot.service.js.map +1 -1
- package/dist/backend/backend/src/services/settings/api-key-access.service.d.ts +126 -0
- package/dist/backend/backend/src/services/settings/api-key-access.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/settings/api-key-access.service.js +223 -0
- package/dist/backend/backend/src/services/settings/api-key-access.service.js.map +1 -0
- package/dist/backend/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/skill/skill-executor.service.js +17 -0
- package/dist/backend/backend/src/services/skill/skill-executor.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +29 -8
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/system/auto-update.service.d.ts +24 -0
- package/dist/backend/backend/src/services/system/auto-update.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/system/auto-update.service.js +78 -8
- package/dist/backend/backend/src/services/system/auto-update.service.js.map +1 -1
- package/dist/backend/backend/src/services/system/auto-update.utils.d.ts +1 -1
- package/dist/backend/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/services/system/auto-update.utils.js.map +1 -1
- package/dist/backend/backend/src/services/system/input-guard-release-check.d.ts +162 -0
- package/dist/backend/backend/src/services/system/input-guard-release-check.d.ts.map +1 -0
- package/dist/backend/backend/src/services/system/input-guard-release-check.js +264 -0
- package/dist/backend/backend/src/services/system/input-guard-release-check.js.map +1 -0
- package/dist/backend/backend/src/services/system/system-control.service.d.ts +36 -0
- package/dist/backend/backend/src/services/system/system-control.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/system/system-control.service.js +109 -10
- package/dist/backend/backend/src/services/system/system-control.service.js.map +1 -1
- package/dist/backend/backend/src/types/skill.types.d.ts +16 -0
- package/dist/backend/backend/src/types/skill.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/skill.types.js.map +1 -1
- package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts +21 -0
- package/dist/backend/backend/src/types/ticket-autopilot.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/ticket-autopilot.types.js +48 -0
- package/dist/backend/backend/src/types/ticket-autopilot.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js +1 -0
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +76 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +82 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/scripts/input-guard-classify.d.ts +44 -0
- package/dist/cli/backend/src/scripts/input-guard-classify.d.ts.map +1 -0
- package/dist/cli/backend/src/scripts/input-guard-classify.js +116 -0
- package/dist/cli/backend/src/scripts/input-guard-classify.js.map +1 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts +8 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js +10 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/crewly-agent-external-runtime.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts +8 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js +21 -0
- package/dist/cli/backend/src/services/agent/crewly-agent/in-process-runtime-registry.js.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js +13 -27
- package/dist/cli/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/drain-queued-delivery.d.ts +59 -0
- package/dist/cli/backend/src/services/messaging/drain-queued-delivery.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/drain-queued-delivery.js +70 -0
- package/dist/cli/backend/src/services/messaging/drain-queued-delivery.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/forced-delivery.d.ts +75 -0
- package/dist/cli/backend/src/services/messaging/forced-delivery.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/forced-delivery.js +87 -0
- package/dist/cli/backend/src/services/messaging/forced-delivery.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/spend-capped-delivery.d.ts +54 -0
- package/dist/cli/backend/src/services/messaging/spend-capped-delivery.d.ts.map +1 -0
- package/dist/cli/backend/src/services/messaging/spend-capped-delivery.js +65 -0
- package/dist/cli/backend/src/services/messaging/spend-capped-delivery.js.map +1 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/skill/skill-executor.service.js +17 -0
- package/dist/cli/backend/src/services/skill/skill-executor.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +29 -8
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/cli/backend/src/services/system/auto-update.utils.d.ts +1 -1
- package/dist/cli/backend/src/services/system/auto-update.utils.d.ts.map +1 -1
- package/dist/cli/backend/src/services/system/auto-update.utils.js.map +1 -1
- package/dist/cli/backend/src/services/system/input-guard-release-check.d.ts +162 -0
- package/dist/cli/backend/src/services/system/input-guard-release-check.d.ts.map +1 -0
- package/dist/cli/backend/src/services/system/input-guard-release-check.js +264 -0
- package/dist/cli/backend/src/services/system/input-guard-release-check.js.map +1 -0
- package/dist/cli/backend/src/types/skill.types.d.ts +16 -0
- package/dist/cli/backend/src/types/skill.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/skill.types.js.map +1 -1
- package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts +21 -0
- package/dist/cli/backend/src/types/ticket-autopilot.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/ticket-autopilot.types.js +48 -0
- package/dist/cli/backend/src/types/ticket-autopilot.types.js.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js +1 -0
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/cli/cli/src/commands/doctor.d.ts +20 -0
- package/dist/cli/cli/src/commands/doctor.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/doctor.js +49 -0
- package/dist/cli/cli/src/commands/doctor.js.map +1 -1
- package/dist/cli/cli/src/index.js +9 -2
- package/dist/cli/cli/src/index.js.map +1 -1
- package/frontend/dist/assets/{index-63a33dbf.js → index-f64b333a.js} +258 -258
- package/frontend/dist/index.html +1 -1
- package/package.json +2 -1
|
@@ -225,9 +225,10 @@ api_call() {
|
|
|
225
225
|
# Include agent session identity header for heartbeat tracking
|
|
226
226
|
# Use ${VAR:-} pattern to avoid 'unbound variable' error under set -u (nounset)
|
|
227
227
|
# The agent badge (#999): the credential that makes the backend treat this
|
|
228
|
-
# call as this agent. Injected by the harness at launch
|
|
229
|
-
# session header
|
|
230
|
-
#
|
|
228
|
+
# call as this agent. Injected by the harness at launch. Without a valid
|
|
229
|
+
# one, the session header counts only when the backend finds this shell's
|
|
230
|
+
# process under that agent's PTY; terminal writes and API-key reads refuse
|
|
231
|
+
# it otherwise (#1024). Never the owner either way.
|
|
231
232
|
if [ -n "${CREWLY_AGENT_BADGE:-}" ]; then
|
|
232
233
|
args+=(-H "X-Agent-Badge: $CREWLY_AGENT_BADGE")
|
|
233
234
|
fi
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Docs Comment
|
|
3
|
+
description: Handle feedback left as comments in a Google Doc — list comments and replies, reply, resolve, or add a comment (via the Google Workspace grant held by Crewly Cloud). Writes as the owner's Google account — say what you posted.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: productivity
|
|
6
|
+
skillType: claude-skill
|
|
7
|
+
assignableRoles:
|
|
8
|
+
- orchestrator
|
|
9
|
+
- team-leader
|
|
10
|
+
- developer
|
|
11
|
+
- operations
|
|
12
|
+
- ops
|
|
13
|
+
- sales
|
|
14
|
+
- support
|
|
15
|
+
- generalist
|
|
16
|
+
triggers:
|
|
17
|
+
- comments in the doc
|
|
18
|
+
- google doc comments
|
|
19
|
+
- reply to the comment
|
|
20
|
+
- resolve the comment
|
|
21
|
+
- feedback in the doc
|
|
22
|
+
- add a comment to the doc
|
|
23
|
+
tags:
|
|
24
|
+
- google
|
|
25
|
+
- docs
|
|
26
|
+
- comments
|
|
27
|
+
execution:
|
|
28
|
+
type: script
|
|
29
|
+
script:
|
|
30
|
+
file: execute.sh
|
|
31
|
+
interpreter: bash
|
|
32
|
+
timeoutMs: 60000
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Docs Comment
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bash execute.sh list --doc "https://docs.google.com/document/d/1AbC…/edit"
|
|
39
|
+
bash execute.sh list --doc 1AbC… --include-resolved
|
|
40
|
+
bash execute.sh reply --doc 1AbC… --comment AAAB… --text "Updated the intro, see paragraph 2."
|
|
41
|
+
bash execute.sh resolve --doc 1AbC… --comment AAAB… --text "Done in v2."
|
|
42
|
+
bash execute.sh resolve --doc 1AbC… --comment AAAB…
|
|
43
|
+
bash execute.sh add --doc 1AbC… --text "Is this number from Q2?" --quote "grew 40%"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`--doc` takes a document id or its docs.google.com URL. `--account` picks a
|
|
47
|
+
connected Google account (see below).
|
|
48
|
+
|
|
49
|
+
## Working through feedback
|
|
50
|
+
|
|
51
|
+
1. `list` the open comments. Each one has the text it is about (`quote`),
|
|
52
|
+
who wrote it and when, and any replies.
|
|
53
|
+
2. Make the change the comment asks for (for example with `docs-write`), or
|
|
54
|
+
decide it needs the owner.
|
|
55
|
+
3. `resolve` the comment with a one-line note of what changed, or `reply`
|
|
56
|
+
with a question if it is unclear. Do not resolve a comment you did not
|
|
57
|
+
address.
|
|
58
|
+
|
|
59
|
+
Everything you post appears under the owner's Google name. Tell the owner
|
|
60
|
+
what you replied to or resolved.
|
|
61
|
+
|
|
62
|
+
## Output
|
|
63
|
+
|
|
64
|
+
`list`:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{"success":true,"docId":"1AbC…","count":1,"truncated":false,"comments":[{"id":"AAAB…","author":"Steve","createdTime":"2026-10-03T09:12:00.000Z","content":"Too long","quote":"In this document we…","anchor":"kix.x1","resolved":false,"replies":[{"id":"AAAC…","author":"Ella","createdTime":"…","content":"Trimmed."}]}]}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Resolved comments are left out unless you pass `--include-resolved`.
|
|
71
|
+
`truncated: true` means the document has more than 500 comments and only the
|
|
72
|
+
first 500 were read.
|
|
73
|
+
|
|
74
|
+
`reply` / `resolve`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{"success":true,"action":"reply","docId":"1AbC…","commentId":"AAAB…","replyId":"AAAD…","content":"Updated the intro."}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`add`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{"success":true,"action":"add","docId":"1AbC…","commentId":"AAAE…","content":"Is this number from Q2?","quote":"grew 40%"}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Comments you add are not anchored
|
|
87
|
+
|
|
88
|
+
Google Docs does not show a comment created through the API as attached to
|
|
89
|
+
text. `--quote` is sent to Google as the quoted text, but in the Docs UI the
|
|
90
|
+
comment **may appear as a general (unanchored) comment** on the whole
|
|
91
|
+
document. When the location matters, quote the passage in the comment text
|
|
92
|
+
too, for example `--text "On 'grew 40%': is this Q2?"`.
|
|
93
|
+
|
|
94
|
+
## Failures
|
|
95
|
+
|
|
96
|
+
`{"success":false,"reason":"reauth_required",…}` (exit 1): the Google grant
|
|
97
|
+
can read comments but not write them on this document. Reply, resolve and add
|
|
98
|
+
on a document Crewly did not create need Google Drive edit access, which
|
|
99
|
+
older connections do not include. Run `google-connect --product drive --channel <chat-channel-id>` to
|
|
100
|
+
post a one-tap card for the owner, and tell them in one line what you will do
|
|
101
|
+
once it is granted. Do not paste a link yourself.
|
|
102
|
+
|
|
103
|
+
`{"success":false,"reason":"not_connected","hint":"…"}` (exit 1): Google
|
|
104
|
+
Drive is not connected. Use `google-connect --product drive` the same way.
|
|
105
|
+
|
|
106
|
+
`{"success":false,"reason":"not_permitted",…}` (exit 1): this Google
|
|
107
|
+
connection belongs to someone else and is not shared with the person you are
|
|
108
|
+
working for. Say so; do not try another account.
|
|
109
|
+
|
|
110
|
+
`reason: "google_error"` with a 404 means the document id or comment id is
|
|
111
|
+
wrong, or the account cannot see the document.
|
|
112
|
+
|
|
113
|
+
## Choosing a Google account
|
|
114
|
+
|
|
115
|
+
Several Google accounts can be connected at once. Without `--account` the call uses the default one (the first you connected, or whichever you marked default on the Connections page). Name one explicitly when it matters:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bash execute.sh list --doc 1AbC… --account work@company.com
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The account must be connected *for this product* — Google consent is per product (Gmail / Calendar / Drive), so an account connected only for Calendar cannot read Drive.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# docs-comment — List, reply to, resolve or add comments in a Google Doc
|
|
4
|
+
#
|
|
5
|
+
# Backed by:
|
|
6
|
+
# GET /api/google/docs/:id/comments[?includeResolved=1]
|
|
7
|
+
# POST /api/google/docs/:id/comments { text, quote? }
|
|
8
|
+
# POST /api/google/docs/:id/comments/:commentId/replies { text }
|
|
9
|
+
# POST /api/google/docs/:id/comments/:commentId/resolve { text? }
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# bash execute.sh list --doc <id|url> [--include-resolved]
|
|
13
|
+
# bash execute.sh reply --doc <id|url> --comment <id> --text "…"
|
|
14
|
+
# bash execute.sh resolve --doc <id|url> --comment <id> [--text "…"]
|
|
15
|
+
# bash execute.sh add --doc <id|url> --text "…" [--quote "exact text"]
|
|
16
|
+
# bash execute.sh '{"command":"list","doc":"<id>"}'
|
|
17
|
+
# =============================================================================
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
source "${SCRIPT_DIR}/../../_common/lib.sh"
|
|
21
|
+
|
|
22
|
+
print_usage() {
|
|
23
|
+
cat <<'EOF_USAGE'
|
|
24
|
+
Usage:
|
|
25
|
+
bash execute.sh list --doc <id|url> [--include-resolved]
|
|
26
|
+
bash execute.sh reply --doc <id|url> --comment <id> --text "…"
|
|
27
|
+
bash execute.sh resolve --doc <id|url> --comment <id> [--text "…"]
|
|
28
|
+
bash execute.sh add --doc <id|url> --text "…" [--quote "exact text in the doc"]
|
|
29
|
+
bash execute.sh '{"command":"reply","doc":"<id>","comment":"<id>","text":"…"}'
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--doc Document id or docs.google.com URL
|
|
33
|
+
--comment Comment id (from list)
|
|
34
|
+
--text Reply / comment text (optional for resolve)
|
|
35
|
+
--quote Exact document text the new comment is about (add only;
|
|
36
|
+
Google Docs may still show it as an unanchored comment)
|
|
37
|
+
--include-resolved list: include resolved comments too
|
|
38
|
+
--account Which connected Google account to act as (default: your primary)
|
|
39
|
+
--help | -h Show this help
|
|
40
|
+
EOF_USAGE
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fail_from() {
|
|
44
|
+
printf '%s' "$1" | jq -c '{success: false, reason: (.details.error // .details // .error // "unknown"), hint: (.details.hint // ""), message: (.details.message // "")}' 2>/dev/null \
|
|
45
|
+
|| jq -n --arg r "$1" '{success: false, reason: $r}'
|
|
46
|
+
exit 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
uri() { jq -rn --arg v "$1" '$v|@uri'; }
|
|
50
|
+
|
|
51
|
+
# api_call may print a one-line warning to stderr (no CREWLY_SESSION_NAME);
|
|
52
|
+
# the backend answer is always the last line. On failure print the mapped
|
|
53
|
+
# failure JSON (fail_from) and return 1.
|
|
54
|
+
call() {
|
|
55
|
+
local out
|
|
56
|
+
out=$(api_call "$@" 2>&1) || { fail_from "$(printf '%s\n' "$out" | tail -n 1)"; }
|
|
57
|
+
printf '%s\n' "$out" | tail -n 1
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
INPUT_JSON=""
|
|
61
|
+
COMMAND=""
|
|
62
|
+
if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
|
|
63
|
+
INPUT_JSON="$1"
|
|
64
|
+
shift || true
|
|
65
|
+
elif [[ $# -gt 0 && ${1:0:1} != '-' ]]; then
|
|
66
|
+
COMMAND="$1"
|
|
67
|
+
shift
|
|
68
|
+
fi
|
|
69
|
+
DOC=""; COMMENT=""; TEXT=""; TEXT_SET=0; QUOTE=""; INCLUDE_RESOLVED=0; ACCOUNT=""
|
|
70
|
+
while [[ $# -gt 0 ]]; do
|
|
71
|
+
case "$1" in
|
|
72
|
+
--doc|--id) [ $# -ge 2 ] || error_exit "$1 requires a value"; DOC="$2"; shift 2 ;;
|
|
73
|
+
--comment) [ $# -ge 2 ] || error_exit "--comment requires a value"; COMMENT="$2"; shift 2 ;;
|
|
74
|
+
--text) [ $# -ge 2 ] || error_exit "--text requires a value"; TEXT="$2"; TEXT_SET=1; shift 2 ;;
|
|
75
|
+
--quote) [ $# -ge 2 ] || error_exit "--quote requires a value"; QUOTE="$2"; shift 2 ;;
|
|
76
|
+
--include-resolved) INCLUDE_RESOLVED=1; shift ;;
|
|
77
|
+
--account) [ $# -ge 2 ] || error_exit "--account requires a value"; ACCOUNT="$2"; shift 2 ;;
|
|
78
|
+
--help|-h) print_usage; exit 0 ;;
|
|
79
|
+
*) error_exit "Unknown option: $1" ;;
|
|
80
|
+
esac
|
|
81
|
+
done
|
|
82
|
+
if [ -n "$INPUT_JSON" ]; then
|
|
83
|
+
INPUT=$(read_json_input "$INPUT_JSON")
|
|
84
|
+
[ -z "$COMMAND" ] && COMMAND=$(printf '%s' "$INPUT" | jq -r '.command // .action // empty')
|
|
85
|
+
[ -z "$DOC" ] && DOC=$(printf '%s' "$INPUT" | jq -r '.doc // .id // .documentId // empty')
|
|
86
|
+
[ -z "$COMMENT" ] && COMMENT=$(printf '%s' "$INPUT" | jq -r '.comment // .commentId // empty')
|
|
87
|
+
[ "$TEXT_SET" -eq 0 ] && TEXT=$(printf '%s' "$INPUT" | jq -r '.text // empty')
|
|
88
|
+
[ -z "$QUOTE" ] && QUOTE=$(printf '%s' "$INPUT" | jq -r '.quote // empty')
|
|
89
|
+
[ "$INCLUDE_RESOLVED" -eq 0 ] && [ "$(printf '%s' "$INPUT" | jq -r '.includeResolved // false')" = "true" ] && INCLUDE_RESOLVED=1
|
|
90
|
+
[ -z "$ACCOUNT" ] && ACCOUNT=$(printf '%s' "$INPUT" | jq -r '.account // empty')
|
|
91
|
+
fi
|
|
92
|
+
# Route the call at one connected Google account; unset means the default.
|
|
93
|
+
[ -n "$ACCOUNT" ] && export CREWLY_GOOGLE_ACCOUNT="$ACCOUNT"
|
|
94
|
+
# Accept a full docs.google.com URL too.
|
|
95
|
+
DOC=$(printf '%s' "$DOC" | sed -E 's#.*/document/d/([^/?]+).*#\1#')
|
|
96
|
+
|
|
97
|
+
[ -n "$COMMAND" ] || error_exit "a command is required: list, reply, resolve or add (see --help)"
|
|
98
|
+
require_param "doc (--doc)" "$DOC"
|
|
99
|
+
BASE="/google/docs/$(uri "$DOC")/comments"
|
|
100
|
+
|
|
101
|
+
case "$COMMAND" in
|
|
102
|
+
list)
|
|
103
|
+
QS=""; [ "$INCLUDE_RESOLVED" -eq 1 ] && QS="?includeResolved=1"
|
|
104
|
+
RESPONSE=$(call GET "${BASE}${QS}") || { printf '%s\n' "$RESPONSE"; exit 1; }
|
|
105
|
+
# Oversized answers come back as the output-cap envelope; pass it through.
|
|
106
|
+
if printf '%s' "$RESPONSE" | jq -e '.truncated == true and has("file")' >/dev/null 2>&1; then printf '%s\n' "$RESPONSE"; exit 0; fi
|
|
107
|
+
printf '%s' "$RESPONSE" | jq -c '{success: (.success // false), docId: .data.docId, count: (.data.comments | length), truncated: (.data.truncated // false), comments: .data.comments}'
|
|
108
|
+
;;
|
|
109
|
+
reply)
|
|
110
|
+
require_param "comment (--comment)" "$COMMENT"
|
|
111
|
+
require_param "text (--text)" "$TEXT"
|
|
112
|
+
BODY=$(jq -cn --arg text "$TEXT" '{text: $text}')
|
|
113
|
+
RESPONSE=$(call POST "${BASE}/$(uri "$COMMENT")/replies" "$BODY") || { printf '%s\n' "$RESPONSE"; exit 1; }
|
|
114
|
+
printf '%s' "$RESPONSE" | jq -c '{success: (.success // false), action: "reply", docId: .data.docId, commentId: .data.commentId, replyId: .data.id, content: .data.content}'
|
|
115
|
+
;;
|
|
116
|
+
resolve)
|
|
117
|
+
require_param "comment (--comment)" "$COMMENT"
|
|
118
|
+
BODY=$(jq -cn --arg text "$TEXT" 'if $text != "" then {text: $text} else {} end')
|
|
119
|
+
RESPONSE=$(call POST "${BASE}/$(uri "$COMMENT")/resolve" "$BODY") || { printf '%s\n' "$RESPONSE"; exit 1; }
|
|
120
|
+
printf '%s' "$RESPONSE" | jq -c '{success: (.success // false), action: "resolve", docId: .data.docId, commentId: .data.commentId, replyId: .data.id, content: (.data.content // ""), resolved: true}'
|
|
121
|
+
;;
|
|
122
|
+
add)
|
|
123
|
+
require_param "text (--text)" "$TEXT"
|
|
124
|
+
BODY=$(jq -cn --arg text "$TEXT" --arg quote "$QUOTE" '{text: $text} + (if $quote != "" then {quote: $quote} else {} end)')
|
|
125
|
+
RESPONSE=$(call POST "${BASE}" "$BODY") || { printf '%s\n' "$RESPONSE"; exit 1; }
|
|
126
|
+
printf '%s' "$RESPONSE" | jq -c '{success: (.success // false), action: "add", docId: .data.docId, commentId: .data.id, content: .data.content} + (if .data.quote then {quote: .data.quote} else {} end)'
|
|
127
|
+
;;
|
|
128
|
+
*)
|
|
129
|
+
error_exit "Unknown command: $COMMAND (expected list, reply, resolve or add)"
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for docs-comment — run with: bash execute.test.sh
|
|
3
|
+
# Python HTTP stub as the backend; asserts the request the skill sends and
|
|
4
|
+
# the JSON it prints. No Google call is made. Exit 0 on pass.
|
|
5
|
+
set -u
|
|
6
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
EXEC="$HERE/execute.sh"
|
|
8
|
+
PASS=0; FAIL=0
|
|
9
|
+
check() {
|
|
10
|
+
local name="$1"; local got="$2"; local want="$3"
|
|
11
|
+
if [ "$got" = "$want" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $name"; echo " want: $want"; echo " got: $got"; fi
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
PORT=18831
|
|
15
|
+
STUB_LOG="$(mktemp)"; export STUB_LOG; export STUB_PORT="$PORT"
|
|
16
|
+
python3 -u <<'PY' >/dev/null 2>&1 &
|
|
17
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
18
|
+
import json, os
|
|
19
|
+
LOG = os.environ['STUB_LOG']; PORT = int(os.environ['STUB_PORT'])
|
|
20
|
+
COMMENT = {'id': 'c1', 'author': 'Steve', 'createdTime': 't0', 'content': 'Too long', 'quote': 'intro',
|
|
21
|
+
'resolved': False, 'replies': [{'id': 'r0', 'author': 'Ella', 'createdTime': 't1', 'content': 'Trimmed'}]}
|
|
22
|
+
class H(BaseHTTPRequestHandler):
|
|
23
|
+
def _send(self, status, payload):
|
|
24
|
+
self.send_response(status); self.send_header('Content-Type', 'application/json'); self.end_headers()
|
|
25
|
+
self.wfile.write(json.dumps(payload).encode())
|
|
26
|
+
def _reply(self, method, data=None):
|
|
27
|
+
open(LOG, 'w').write(json.dumps({'method': method, 'path': self.path, 'body': data,
|
|
28
|
+
'account': self.headers.get('X-Google-Account')}))
|
|
29
|
+
if '/docs/narrow/' in self.path:
|
|
30
|
+
return self._send(403, {'success': False, 'error': 'reauth_required',
|
|
31
|
+
'message': 'Replying to or adding comments on this document needs Google Drive edit access, which this Google account has not granted yet.',
|
|
32
|
+
'hint': 'Ask the owner to reconnect Google Drive: run the google-connect skill with --product drive.'})
|
|
33
|
+
if method == 'GET':
|
|
34
|
+
return self._send(200, {'success': True, 'data': {'docId': 'd1', 'comments': [COMMENT], 'truncated': False}})
|
|
35
|
+
if self.path.endswith('/replies'):
|
|
36
|
+
return self._send(200, {'success': True, 'data': {'docId': 'd1', 'commentId': 'c1', 'id': 'r9', 'content': data['text']}})
|
|
37
|
+
if self.path.endswith('/resolve'):
|
|
38
|
+
return self._send(200, {'success': True, 'data': {'docId': 'd1', 'commentId': 'c1', 'id': 'r10', 'action': 'resolve', 'content': data.get('text', ''), 'resolved': True}})
|
|
39
|
+
out = {'docId': 'd1', 'id': 'c7', 'content': data['text'], 'resolved': False, 'replies': []}
|
|
40
|
+
if 'quote' in data: out['quote'] = data['quote']
|
|
41
|
+
return self._send(200, {'success': True, 'data': out})
|
|
42
|
+
def do_GET(self): self._reply('GET')
|
|
43
|
+
def do_POST(self):
|
|
44
|
+
n = int(self.headers.get('content-length', '0')); body = self.rfile.read(n).decode() if n else ''
|
|
45
|
+
self._reply('POST', json.loads(body or '{}'))
|
|
46
|
+
def log_message(self, *a, **k): pass
|
|
47
|
+
HTTPServer(('127.0.0.1', PORT), H).serve_forever()
|
|
48
|
+
PY
|
|
49
|
+
STUB_PID=$!
|
|
50
|
+
disown "$STUB_PID" 2>/dev/null || true
|
|
51
|
+
trap 'kill "$STUB_PID" >/dev/null 2>&1; rm -f "$STUB_LOG"' EXIT
|
|
52
|
+
for i in $(seq 1 30); do curl -s -o /dev/null -m 0.2 "http://127.0.0.1:${PORT}/" 2>/dev/null && break; sleep 0.1; done
|
|
53
|
+
|
|
54
|
+
run() { CREWLY_API_URL="http://127.0.0.1:${PORT}" bash "$EXEC" "$@" 2>/dev/null; }
|
|
55
|
+
run_err() { CREWLY_API_URL="http://127.0.0.1:${PORT}" bash "$EXEC" "$@" 2>&1 >/dev/null; }
|
|
56
|
+
req() { jq -r '.method + " " + .path + " " + (.body|tojson)' "$STUB_LOG"; }
|
|
57
|
+
|
|
58
|
+
# list
|
|
59
|
+
OUT=$(run list --doc "https://docs.google.com/document/d/d1/edit?tab=t.0")
|
|
60
|
+
check "list: request" "$(req)" 'GET /api/google/docs/d1/comments null'
|
|
61
|
+
check "list: output" "$(printf '%s' "$OUT" | jq -c '{success, docId, count, truncated, first: .comments[0].id, quote: .comments[0].quote, replies: (.comments[0].replies|length)}')" \
|
|
62
|
+
'{"success":true,"docId":"d1","count":1,"truncated":false,"first":"c1","quote":"intro","replies":1}'
|
|
63
|
+
run list --doc d1 --include-resolved >/dev/null
|
|
64
|
+
check "list: --include-resolved" "$(req)" 'GET /api/google/docs/d1/comments?includeResolved=1 null'
|
|
65
|
+
|
|
66
|
+
# reply
|
|
67
|
+
OUT=$(run reply --doc d1 --comment c1 --text "Fixed, see para 2")
|
|
68
|
+
check "reply: request" "$(req)" 'POST /api/google/docs/d1/comments/c1/replies {"text":"Fixed, see para 2"}'
|
|
69
|
+
check "reply: output" "$OUT" '{"success":true,"action":"reply","docId":"d1","commentId":"c1","replyId":"r9","content":"Fixed, see para 2"}'
|
|
70
|
+
|
|
71
|
+
# resolve, with and without a message
|
|
72
|
+
OUT=$(run resolve --doc d1 --comment c1 --text "Done")
|
|
73
|
+
check "resolve: request" "$(req)" 'POST /api/google/docs/d1/comments/c1/resolve {"text":"Done"}'
|
|
74
|
+
check "resolve: output" "$OUT" '{"success":true,"action":"resolve","docId":"d1","commentId":"c1","replyId":"r10","content":"Done","resolved":true}'
|
|
75
|
+
run resolve --doc d1 --comment c1 >/dev/null
|
|
76
|
+
check "resolve: no text" "$(req)" 'POST /api/google/docs/d1/comments/c1/resolve {}'
|
|
77
|
+
|
|
78
|
+
# add, with and without a quote
|
|
79
|
+
OUT=$(run add --doc d1 --text "Source?" --quote "grew 40%")
|
|
80
|
+
check "add: request" "$(req)" 'POST /api/google/docs/d1/comments {"text":"Source?","quote":"grew 40%"}'
|
|
81
|
+
check "add: output" "$OUT" '{"success":true,"action":"add","docId":"d1","commentId":"c7","content":"Source?","quote":"grew 40%"}'
|
|
82
|
+
run add --doc d1 --text "General" >/dev/null
|
|
83
|
+
check "add: no quote" "$(req)" 'POST /api/google/docs/d1/comments {"text":"General"}'
|
|
84
|
+
|
|
85
|
+
# JSON input + --account
|
|
86
|
+
run '{"command":"reply","doc":"d1","comment":"c1","text":"via json","account":"work@x.com"}' >/dev/null
|
|
87
|
+
check "json input: request" "$(req)" 'POST /api/google/docs/d1/comments/c1/replies {"text":"via json"}'
|
|
88
|
+
check "json input: account header" "$(jq -r '.account' "$STUB_LOG")" 'work@x.com'
|
|
89
|
+
|
|
90
|
+
# reauth_required is passed through with its English hint, exit 1
|
|
91
|
+
OUT=$(run reply --doc narrow --comment c1 --text "x"); CODE=$?
|
|
92
|
+
check "reauth: exit code" "$CODE" "1"
|
|
93
|
+
check "reauth: reason" "$(printf '%s' "$OUT" | jq -r '.reason')" "reauth_required"
|
|
94
|
+
check "reauth: hint names the one-tap path" "$(printf '%s' "$OUT" | jq -r '.hint' | grep -c 'google-connect skill with --product drive')" "1"
|
|
95
|
+
|
|
96
|
+
# argument validation (no request sent)
|
|
97
|
+
check "no command" "$(run_err --doc d1 | grep -c 'command is required')" "1"
|
|
98
|
+
check "unknown command" "$(run_err delete --doc d1 | grep -c 'Unknown command')" "1"
|
|
99
|
+
check "reply needs --comment" "$(run_err reply --doc d1 --text x | grep -c 'comment')" "1"
|
|
100
|
+
check "reply needs --text" "$(run_err reply --doc d1 --comment c1 | grep -c 'text')" "1"
|
|
101
|
+
check "add needs --text" "$(run_err add --doc d1 | grep -c 'text')" "1"
|
|
102
|
+
check "needs --doc" "$(run_err list | grep -c 'doc')" "1"
|
|
103
|
+
|
|
104
|
+
echo "docs-comment: $PASS passed, $FAIL failed"
|
|
105
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -193,6 +193,18 @@ orchestrator.
|
|
|
193
193
|
`--retro on|off|default` switches the team lead's daily retro (default: on
|
|
194
194
|
while an autopilot experiment on the project is running).
|
|
195
195
|
|
|
196
|
+
When the project has a goal (its goals log or an active project OKR) but
|
|
197
|
+
nothing is left to triage and someone on the team is idle, the team lead gets
|
|
198
|
+
ONE `goal_replan` WorkItem: the goal, the tickets closed in the last 7 days,
|
|
199
|
+
any open experiment card, and the ask "open the next tickets toward this goal,
|
|
200
|
+
or say why there are none". The lead opens the tickets; the autopilot never
|
|
201
|
+
makes them ready or starts work. At most `--replans-per-day <0-5>` a day
|
|
202
|
+
(default 1; 0 = off), within the same daily budget and in-progress cap. A
|
|
203
|
+
replan still open after `--replan-ttl-hours` (default 4) is expired. After a
|
|
204
|
+
replan that opened no tickets the autopilot backs off (skips 2 days, then 4,
|
|
205
|
+
then 7) until a new goal / OKR is set or a ticket is created. Only goals-log
|
|
206
|
+
entries from the last 30 days count as an active goal.
|
|
207
|
+
|
|
196
208
|
## Autopilot numbers and retro (owner / orchestrator / team lead)
|
|
197
209
|
|
|
198
210
|
```bash
|
|
@@ -201,7 +213,7 @@ bash execute.sh runs --project P [--days 7] [--label feed] # run trace + tic
|
|
|
201
213
|
```
|
|
202
214
|
|
|
203
215
|
`stats` counts tickets triaged / started / done (worker finished) / verified
|
|
204
|
-
(ticket done) / sent back / stalled, cycle times, owner touches by kind,
|
|
216
|
+
(ticket done) / sent back / stalled, goal replans, cycle times, owner touches by kind,
|
|
205
217
|
stalls by cause, harness interventions, traced tokens and $ against the daily
|
|
206
218
|
budget, and time paused on the budget. Read a day's run with `trace-read
|
|
207
219
|
--trace <runTraceId>`.
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
# bash execute.sh ask-owner --project P --id APP-12 --clear [--note "answer"]
|
|
24
24
|
# bash execute.sh autopilot --project P [--on|--off] [--driver <session>|--driver default]
|
|
25
25
|
# [--daily-budget <tokens, e.g. 20M>] [--max-in-flight <n>] (owner / orchestrator)
|
|
26
|
-
# [--retro on|off|default]
|
|
26
|
+
# [--retro on|off|default] [--replans-per-day <0-5>|default]
|
|
27
|
+
# [--replan-ttl-hours <1-48>|default]
|
|
27
28
|
# bash execute.sh stats --project P [--days 14] [--label feed] (owner / orc / lead)
|
|
28
29
|
# bash execute.sh runs --project P [--days 7] [--label feed] (owner / orc / lead)
|
|
29
30
|
# bash execute.sh retro --project P --day YYYY-MM-DD --summary "…" [--problem "class|title|detail|evidence" …]
|
|
@@ -63,10 +64,15 @@ Usage:
|
|
|
63
64
|
The owner answered: remove the needs-owner mark
|
|
64
65
|
bash execute.sh autopilot --project P [--on|--off] [--driver <session>|default]
|
|
65
66
|
[--daily-budget <tokens, e.g. 20M>] [--max-in-flight <n>] [--retro on|off|default]
|
|
67
|
+
[--replans-per-day <0-5>|default] [--replan-ttl-hours <1-48>|default]
|
|
66
68
|
Owner / orchestrator: show or change the ticket
|
|
67
69
|
autopilot (no flags = show). --retro: the lead's
|
|
68
70
|
daily retro (default: on while an autopilot
|
|
69
|
-
experiment runs)
|
|
71
|
+
experiment runs). --replans-per-day: how often a
|
|
72
|
+
day the lead is woken to plan toward the goal when
|
|
73
|
+
nothing is left to triage (default 1, 0 = off);
|
|
74
|
+
--replan-ttl-hours: a replan still open after this
|
|
75
|
+
long is expired (default 4)
|
|
70
76
|
bash execute.sh stats --project P [--days 14] [--label feed]
|
|
71
77
|
Autopilot numbers per day: tickets triaged /
|
|
72
78
|
started / done / verified / sent back / stalled,
|
|
@@ -90,7 +96,7 @@ STATUS=""; SOURCE=""; REQUEST_ID=""; NOTE=""; OWNER_REVIEW=""; ASSIGNEE=""; STAR
|
|
|
90
96
|
ACCEPTANCE_JSON="null"
|
|
91
97
|
HAS_DESCRIPTION=0
|
|
92
98
|
QUESTION=""; CLEAR=""; OPTIONS_JSON="[]"; DEFAULT_OPT=""; DEADLINE=""; SENSITIVE=""; AP_ENABLED=""; AP_DRIVER=""; AP_BUDGET=""; AP_MAX=""
|
|
93
|
-
AP_RETRO=""; DAYS=""; LABEL=""; DAY=""; SUMMARY=""; PROBLEMS_JSON="[]"
|
|
99
|
+
AP_RETRO=""; AP_REPLANS=""; AP_REPLAN_TTL=""; DAYS=""; LABEL=""; DAY=""; SUMMARY=""; PROBLEMS_JSON="[]"
|
|
94
100
|
|
|
95
101
|
if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
|
|
96
102
|
J="$1"; shift
|
|
@@ -124,6 +130,8 @@ if [[ $# -gt 0 && ${1:0:1} == '{' ]]; then
|
|
|
124
130
|
AP_BUDGET=$(printf '%s' "$J" | jq -r '.dailyBudgetTokens // empty')
|
|
125
131
|
AP_MAX=$(printf '%s' "$J" | jq -r '.maxInFlightPerMember // empty')
|
|
126
132
|
AP_RETRO=$(printf '%s' "$J" | jq -r 'if (.retro|type) == "boolean" then (if .retro then "on" else "off" end) else (.retro // empty) end')
|
|
133
|
+
AP_REPLANS=$(printf '%s' "$J" | jq -r '.replansPerDay // empty')
|
|
134
|
+
AP_REPLAN_TTL=$(printf '%s' "$J" | jq -r '.replanTtlHours // empty')
|
|
127
135
|
DAYS=$(printf '%s' "$J" | jq -r '.days // empty')
|
|
128
136
|
LABEL=$(printf '%s' "$J" | jq -r '.label // empty')
|
|
129
137
|
DAY=$(printf '%s' "$J" | jq -r '.day // empty')
|
|
@@ -166,6 +174,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
166
174
|
[ $# -ge 2 ] || error_exit "--daily-budget requires a value"; AP_BUDGET="$2"; shift 2 ;;
|
|
167
175
|
--max-in-flight) [ $# -ge 2 ] || error_exit "--max-in-flight requires a value"; AP_MAX="$2"; shift 2 ;;
|
|
168
176
|
--retro) [ $# -ge 2 ] || error_exit "--retro requires on, off or default"; AP_RETRO="$2"; shift 2 ;;
|
|
177
|
+
--replans-per-day)
|
|
178
|
+
[ $# -ge 2 ] || error_exit "--replans-per-day requires 0-5 or default"; AP_REPLANS="$2"; shift 2 ;;
|
|
179
|
+
--replan-ttl-hours)
|
|
180
|
+
[ $# -ge 2 ] || error_exit "--replan-ttl-hours requires 1-48 or default"; AP_REPLAN_TTL="$2"; shift 2 ;;
|
|
169
181
|
--days) [ $# -ge 2 ] || error_exit "--days requires a value"; DAYS="$2"; shift 2 ;;
|
|
170
182
|
--label) [ $# -ge 2 ] || error_exit "--label requires a value"; LABEL="$2"; shift 2 ;;
|
|
171
183
|
--day) [ $# -ge 2 ] || error_exit "--day requires a value"; DAY="$2"; shift 2 ;;
|
|
@@ -282,13 +294,15 @@ case "$ACTION" in
|
|
|
282
294
|
;;
|
|
283
295
|
autopilot)
|
|
284
296
|
require_param "project" "$PROJECT"
|
|
285
|
-
if [ -z "$AP_ENABLED$AP_DRIVER$AP_BUDGET$AP_MAX$AP_RETRO" ]; then
|
|
297
|
+
if [ -z "$AP_ENABLED$AP_DRIVER$AP_BUDGET$AP_MAX$AP_RETRO$AP_REPLANS$AP_REPLAN_TTL" ]; then
|
|
286
298
|
api_call GET "/project-ticket-autopilot/$(enc "$PROJECT")" | jq '{success, autopilot: .data}'
|
|
287
299
|
else
|
|
288
|
-
BODY=$(jq -n --arg enabled "$AP_ENABLED" --arg driver "$AP_DRIVER" --arg budget "$AP_BUDGET" --arg max "$AP_MAX" --arg retro "$AP_RETRO" \
|
|
300
|
+
BODY=$(jq -n --arg enabled "$AP_ENABLED" --arg driver "$AP_DRIVER" --arg budget "$AP_BUDGET" --arg max "$AP_MAX" --arg retro "$AP_RETRO" --arg replans "$AP_REPLANS" --arg ttl "$AP_REPLAN_TTL" \
|
|
289
301
|
'{}
|
|
290
302
|
+ (if $enabled != "" then {enabled: ($enabled == "true")} else {} end)
|
|
291
303
|
+ (if $retro == "default" then {retro: null} elif $retro != "" then {retro: $retro} else {} end)
|
|
304
|
+
+ (if $replans == "default" then {replansPerDay: null} elif $replans != "" then {replansPerDay: ($replans | tonumber? // $replans)} else {} end)
|
|
305
|
+
+ (if $ttl == "default" then {replanTtlHours: null} elif $ttl != "" then {replanTtlHours: ($ttl | tonumber? // $ttl)} else {} end)
|
|
292
306
|
+ (if $driver == "default" then {driver: null} elif $driver != "" then {driver: $driver} else {} end)
|
|
293
307
|
+ (if $budget == "default" then {dailyBudgetTokens: null} elif $budget != "" then {dailyBudgetTokens: ($budget | tonumber? // $budget)} else {} end)
|
|
294
308
|
+ (if $max == "default" then {maxInFlightPerMember: null} elif $max != "" then {maxInFlightPerMember: ($max | tonumber? // $max)} else {} end)')
|
|
@@ -303,7 +317,7 @@ case "$ACTION" in
|
|
|
303
317
|
if [ "$ACTION" = "stats" ]; then
|
|
304
318
|
api_call GET "/project-ticket-autopilot/$(enc "$PROJECT")/stats${QS:+?$QS}" \
|
|
305
319
|
| jq '{success, stats: (.data | if . == null then null else {project, label, range, pausedForToday, total, labels,
|
|
306
|
-
days: [.days[] | {day, triaged, started, done, verified, sentBack, stalled, ownerTouches: .ownerTouches.total, stallMs: .stalls.totalMs, costUsd, pausedMs, runTraceId}]} end)}'
|
|
320
|
+
days: [.days[] | {day, triaged, replans, started, done, verified, sentBack, stalled, ownerTouches: .ownerTouches.total, stallMs: .stalls.totalMs, costUsd, pausedMs, runTraceId}]} end)}'
|
|
307
321
|
else
|
|
308
322
|
api_call GET "/project-ticket-autopilot/$(enc "$PROJECT")/runs${QS:+?$QS}" | jq '{success, runs: .data}'
|
|
309
323
|
fi
|
|
@@ -32,7 +32,7 @@ class H(BaseHTTPRequestHandler):
|
|
|
32
32
|
return self.reply(200, {'success': True, 'data': T})
|
|
33
33
|
return self.reply(200, {'success': True, 'data': {'project': {'id': 'p1'}, 'tickets': [T], 'invalid': []}})
|
|
34
34
|
if self.path.startswith('/api/project-ticket-autopilot/') and '/stats' in self.path:
|
|
35
|
-
day = {'day': '2026-10-02', 'triaged': 3, 'started': 2, 'done': 2, 'verified': 1, 'sentBack': 0, 'stalled': 1,
|
|
35
|
+
day = {'day': '2026-10-02', 'triaged': 3, 'replans': 1, 'started': 2, 'done': 2, 'verified': 1, 'sentBack': 0, 'stalled': 1,
|
|
36
36
|
'ownerTouches': {'total': 2}, 'stalls': {'totalMs': 60000}, 'costUsd': 1.5, 'pausedMs': 0, 'runTraceId': 'tr-x', 'extra': 1}
|
|
37
37
|
return self.reply(200, {'success': True, 'data': {'project': {'id': 'p1'}, 'label': 'feed', 'range': {}, 'pausedForToday': False,
|
|
38
38
|
'total': {'verified': 1}, 'labels': ['feed'], 'days': [day]}})
|
|
@@ -158,11 +158,17 @@ run autopilot --project p1 --retro on >/dev/null
|
|
|
158
158
|
check "autopilot retro on: body" "$(last '.body | tostring')" '{"retro":"on"}'
|
|
159
159
|
run autopilot --project p1 --retro default >/dev/null
|
|
160
160
|
check "autopilot retro default: body" "$(last '.body | tostring')" '{"retro":null}'
|
|
161
|
+
run autopilot --project p1 --replans-per-day 2 >/dev/null
|
|
162
|
+
check "autopilot replans: body" "$(last '.body | tostring')" '{"replansPerDay":2}'
|
|
163
|
+
run autopilot --project p1 --replans-per-day default >/dev/null
|
|
164
|
+
check "autopilot replans default: body" "$(last '.body | tostring')" '{"replansPerDay":null}'
|
|
165
|
+
run autopilot --project p1 --replan-ttl-hours 6 >/dev/null
|
|
166
|
+
check "autopilot replan ttl: body" "$(last '.body | tostring')" '{"replanTtlHours":6}'
|
|
161
167
|
|
|
162
168
|
# --- stats / runs / retro (specs/2026-10-03-autopilot-experiments.md) ---
|
|
163
169
|
OUT=$(run stats --project p1 --days 7 --label feed)
|
|
164
170
|
check "stats: path" "$(last .path)" "/api/project-ticket-autopilot/p1/stats?days=7&label=feed"
|
|
165
|
-
check "stats: day row" "$(printf '%s' "$OUT" | jq -c '.stats.days[0] | [.day, .verified, .ownerTouches, .stallMs, .runTraceId, has("extra")]')" '["2026-10-02",1,2,60000,"tr-x",false]'
|
|
171
|
+
check "stats: day row" "$(printf '%s' "$OUT" | jq -c '.stats.days[0] | [.day, .verified, .replans, .ownerTouches, .stallMs, .runTraceId, has("extra")]')" '["2026-10-02",1,1,2,60000,"tr-x",false]'
|
|
166
172
|
OUT=$(run runs --project p1)
|
|
167
173
|
check "runs: path" "$(last .path)" "/api/project-ticket-autopilot/p1/runs"
|
|
168
174
|
check "runs: output" "$(printf '%s' "$OUT" | jq -c '.runs.days[0].runTraceId')" '"tr-x"'
|
|
@@ -145,6 +145,39 @@ export declare const TICKET_AUTOPILOT_CONSTANTS: {
|
|
|
145
145
|
/** Option labels of that card */
|
|
146
146
|
readonly RETRO_APPROVE_LABEL: "Approve";
|
|
147
147
|
readonly RETRO_SKIP_LABEL: "Skip";
|
|
148
|
+
/** WorkItem type / `metadata.kind` of the goal replan the driver receives when nothing is left to triage */
|
|
149
|
+
readonly REPLAN_WORK_ITEM_TYPE: "goal_replan";
|
|
150
|
+
readonly REPLAN_METADATA_KIND: "goal_replan";
|
|
151
|
+
/** Default and largest number of goal replans per project per local day (0 = off) */
|
|
152
|
+
readonly DEFAULT_REPLANS_PER_DAY: 1;
|
|
153
|
+
readonly REPLANS_PER_DAY_LIMIT: 5;
|
|
154
|
+
/** Tickets closed (done / cancelled) this many days back are listed in the replan brief */
|
|
155
|
+
readonly REPLAN_CLOSED_LOOKBACK_DAYS: 7;
|
|
156
|
+
/** Most closed tickets listed in the replan brief */
|
|
157
|
+
readonly REPLAN_MAX_CLOSED_TICKETS: 20;
|
|
158
|
+
/** Most characters of goal text quoted in the replan brief */
|
|
159
|
+
readonly REPLAN_GOAL_MAX_CHARS: 2000;
|
|
160
|
+
/** Most goals.md entries quoted (newest first) */
|
|
161
|
+
readonly REPLAN_GOAL_MAX_ENTRIES: 3;
|
|
162
|
+
/** Most open experiment cards listed in the replan brief */
|
|
163
|
+
readonly REPLAN_MAX_EXPERIMENTS: 5;
|
|
164
|
+
/**
|
|
165
|
+
* A replan still live (any live status) after this many hours is expired:
|
|
166
|
+
* cancelled where the pool allows it, and never counted as live again, so
|
|
167
|
+
* it cannot hold triage forever. Default and bounds of `replanTtlHours`.
|
|
168
|
+
*/
|
|
169
|
+
readonly DEFAULT_REPLAN_TTL_HOURS: 4;
|
|
170
|
+
readonly REPLAN_TTL_HOURS_LIMIT: 48;
|
|
171
|
+
/** A live replan older than this stops holding triage once there are tickets to triage (ms) */
|
|
172
|
+
readonly REPLAN_YIELD_AFTER_MS: number;
|
|
173
|
+
/**
|
|
174
|
+
* After a replan that opened no tickets: skip the next 2 days, then 4,
|
|
175
|
+
* then 7 (doubling, capped). Reset by a new goal / OKR or a new ticket.
|
|
176
|
+
*/
|
|
177
|
+
readonly REPLAN_BACKOFF_FIRST_DAYS: 2;
|
|
178
|
+
readonly REPLAN_BACKOFF_MAX_DAYS: 7;
|
|
179
|
+
/** Goals-log entries older than this are not an active goal (days); OKRs keep their own status */
|
|
180
|
+
readonly REPLAN_GOAL_ACTIVE_DAYS: 30;
|
|
148
181
|
};
|
|
149
182
|
/**
|
|
150
183
|
* Safe restart: drain in-flight agent turns before a shutdown kills the PTYs,
|
|
@@ -4148,6 +4181,16 @@ export declare const GOOGLE_WORKSPACE_CONSTANTS: {
|
|
|
4148
4181
|
readonly DRIVE_MAX_CONTENT_BYTES: number;
|
|
4149
4182
|
/** Google-native MIME types and what they export to as text */
|
|
4150
4183
|
readonly DRIVE_EXPORT_MIME: Record<string, string>;
|
|
4184
|
+
/**
|
|
4185
|
+
* Full Drive scope. Needed to reply to, resolve or add comments on a
|
|
4186
|
+
* document Crewly did not create (`comments.create` / `replies.create`
|
|
4187
|
+
* accept only `drive` or `drive.file`). Optional on the Cloud grant.
|
|
4188
|
+
*/
|
|
4189
|
+
readonly DRIVE_FULL_SCOPE: "https://www.googleapis.com/auth/drive";
|
|
4190
|
+
/** Comments fetched per Drive `comments.list` page (Google's max is 100) */
|
|
4191
|
+
readonly COMMENTS_PAGE_SIZE: 100;
|
|
4192
|
+
/** Pages read per `docs-comment list` before stopping (caps a huge thread) */
|
|
4193
|
+
readonly COMMENTS_MAX_PAGES: 5;
|
|
4151
4194
|
/** Sheets range used when the caller names none */
|
|
4152
4195
|
readonly SHEETS_DEFAULT_RANGE: "A1:Z1000";
|
|
4153
4196
|
/** Cap on rows accepted per Sheets write */
|
|
@@ -4174,6 +4217,8 @@ export declare const GOOGLE_WORKSPACE_CONSTANTS: {
|
|
|
4174
4217
|
readonly NETWORK: "network";
|
|
4175
4218
|
/** Caller sent an invalid request */
|
|
4176
4219
|
readonly VALIDATION: "validation";
|
|
4220
|
+
/** The grant lacks a scope this call needs; the owner must reconnect the product */
|
|
4221
|
+
readonly REAUTH_REQUIRED: "reauth_required";
|
|
4177
4222
|
};
|
|
4178
4223
|
};
|
|
4179
4224
|
/**
|
|
@@ -6325,4 +6370,35 @@ export declare const EXPERIMENT_CONSTANTS: {
|
|
|
6325
6370
|
readonly CHECK_IN_RETRY_MIN_MS: number;
|
|
6326
6371
|
readonly CHECK_IN_RETRY_MAX_MS: number;
|
|
6327
6372
|
};
|
|
6373
|
+
/**
|
|
6374
|
+
* Release input-guard check (specs/2026-10-04-release-input-guard-check.md):
|
|
6375
|
+
* classify every live agent's input box with the NEW build before restarting.
|
|
6376
|
+
*/
|
|
6377
|
+
export declare const INPUT_GUARD_CHECK_CONSTANTS: {
|
|
6378
|
+
/** Script of the new build that classifies views, relative to a build's `dist/` */
|
|
6379
|
+
readonly SCRIPT_RELATIVE: "backend/backend/src/scripts/input-guard-classify.js";
|
|
6380
|
+
/** Longest the child process may run (ms) */
|
|
6381
|
+
readonly CHILD_TIMEOUT_MS: number;
|
|
6382
|
+
/** Largest child output accepted (bytes) */
|
|
6383
|
+
readonly CHILD_MAX_OUTPUT_BYTES: number;
|
|
6384
|
+
/** Env switch: skip the gate in the auto-update path */
|
|
6385
|
+
readonly SKIP_ENV: "CREWLY_SKIP_INPUT_GUARD_CHECK";
|
|
6386
|
+
/** Once-per-version "blocked" ledger under CREWLY_HOME */
|
|
6387
|
+
readonly LEDGER_FILE: "input-guard-blocked.json";
|
|
6388
|
+
/** Route (under /api) */
|
|
6389
|
+
readonly ROUTE: "/system/input-guard-check";
|
|
6390
|
+
/** Marker of a build installed on disk but not restarted onto, under CREWLY_HOME */
|
|
6391
|
+
readonly BLOCKED_BUILD_FILE: "blocked-build.json";
|
|
6392
|
+
/** Only the bottom N non-empty screen rows are searched for markers (a transcript can quote anything) */
|
|
6393
|
+
readonly TAIL_LINES: 12;
|
|
6394
|
+
/**
|
|
6395
|
+
* Lower-case footer / composer hints that mean "this runtime is at its chat
|
|
6396
|
+
* prompt": Claude Code's status footer (`⏵⏵ … (shift+tab to cycle)`), the
|
|
6397
|
+
* `? for shortcuts` hint (Codex, Antigravity, Gemini) and Gemini's composer
|
|
6398
|
+
* placeholder. Only with one of these on screen is a missing box a failure.
|
|
6399
|
+
*/
|
|
6400
|
+
readonly READY_MARKERS: readonly string[];
|
|
6401
|
+
/** Lower-case text of dialogs, login and picker screens (no input box expected) */
|
|
6402
|
+
readonly DIALOG_MARKERS: readonly string[];
|
|
6403
|
+
};
|
|
6328
6404
|
//# sourceMappingURL=constants.d.ts.map
|